Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update/Add security headers #280

Open
wants to merge 27 commits into
base: main
Choose a base branch
from
Open

Update/Add security headers #280

wants to merge 27 commits into from

Conversation

max-debug022
Copy link
Contributor

@max-debug022 max-debug022 commented Jun 26, 2024

This PR improves/updates the HTTP-Headers

TLDR

  • Update helmet to 7.1.0 in API and Admin
  • Coherent CORS-Policy (Cross-Origin-Ressource-Sharing) for the API -> to Admin & Site
  • Coherent CORP/COEP (Cross-Origin-Ressource/Embedder-Policy) for API, Admin & Site
  • Strict COOP (Cross-Origin-Opener-Policy) for API, Admin & Site
  • Removal of deprecated, redundant, or prohibited headers
  • CSP (Content Security Policy) that enforces the JEA (just enough access) principle
This PR implements the "Just-Enough-Access" (JEA) security strategy, ensuring that only necessary privileges are granted while restricting all others. The PR includes a coherent CORS policy so that resources can only be shared within the microservices. Additionally, CORP/COEP have been enforced to control resource access and embedding to authorized origins only. Several Content Security Policy (CSP) errors have been resolved & unnecessary privileges have been removed - Admin has slightly more flexibility than the Site. Reasons for several changes:
  • Update helmet to v7.1.0
  • CORS uses now a maxAge of 600s as Chromium (prior to v76) caps at 10mins
  • CORP only for Same-Site
  • Set COOP for same-origin
  • Remove the empty exposedHeaders field as it is the default
  • Disable unused x-powered-by header
  • Disable deprecated X-XSS-Protection Header
  • Disable deprecated X-Frame-Options Header
  • Enable HSTS with recommended values
  • Disable Referrer Policy
  • ...

@thomasdax98
Copy link
Member

Did you test this in one of our projects in a deployed setting? I fear that there might be some unwanted side-effects relating to different domains in a deployed setting

@max-debug022
Copy link
Contributor Author

Did you test this in one of our projects in a deployed setting? I fear that there might be some unwanted side-effects relating to different domains in a deployed setting

Yes, I tested it on deployed settings.

@dkarnutsch
Copy link
Contributor

Did you test this in one of our projects in a deployed setting? I fear that there might be some unwanted side-effects relating to different domains in a deployed setting

I would not add this to existing projects without extensive testing. However, the starter can define best practices and hopefully all bugs are discovered during development. So I think it is relatively safe to merge this.

@dkarnutsch
Copy link
Contributor

dkarnutsch commented Jul 1, 2024

Some header could possible have a comment, when to extend/change them.

You could also add links to best practices (MDN?), so we can track changes.

@max-debug022
Copy link
Contributor Author

max-debug022 commented Jul 1, 2024

Some header could possible have a comment, when to extend/change them.

You could also add links to best practices (MDN?), so we can track changes.

Luckily I originally added comments to each Header but removed them since I wasn't sure if comments should be in the code. I could add them again, but I think it would be better to add detailed docs on how to modify each header and what effect each header has. What would you prefer?

@dkarnutsch
Copy link
Contributor

Some header could possible have a comment, when to extend/change them.
You could also add links to best practices (MDN?), so we can track changes.

Luckily I originally added comments to each Header but removed them since I wasn't sure if comments should be in the code. I could add them again, but I think it would be better to add detailed docs on how to modify each header and what effect each header has. What would you prefer?

In code, as documentation can drift away. @johnnyomair?

@johnnyomair
Copy link
Collaborator

Some header could possible have a comment, when to extend/change them.
You could also add links to best practices (MDN?), so we can track changes.

Luckily I originally added comments to each Header but removed them since I wasn't sure if comments should be in the code. I could add them again, but I think it would be better to add detailed docs on how to modify each header and what effect each header has. What would you prefer?

In code, as documentation can drift away. @johnnyomair?

Agreed. We could add line comments for each header/item.

site/next.config.js Outdated Show resolved Hide resolved
admin/server/server.js Outdated Show resolved Hide resolved
admin/server/server.js Outdated Show resolved Hide resolved
admin/server/server.js Outdated Show resolved Hide resolved
admin/server/server.js Outdated Show resolved Hide resolved
api/src/app.module.ts Show resolved Hide resolved
"style-src-elem 'unsafe-inline'",
"style-src-attr 'unsafe-inline'",
`img-src 'self' data: ${process.env.API_URL}/`,
"script-src 'unsafe-eval'",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we allow 'unsafe-eval' for scripts? Isn't that potentially dangerous? Or is it required by Next?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, it seems like it is required by one of our dependencies.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we specify which dependency? As far as I know Tag Managers require this value, but we don't have one as hard dependency. Nevertheless we might assume that a Tag Manager will be used most of the time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, it's a lot; and I do not know an efficient way to evaluate it (even with tools like bundle-analyzer). Just because a package contains eval or any other "unsafe-eval" code doesn't necessarily mean the CSP needs to permit it. We have hundreds of files (including package dependencies oc) that include some form of eval.

A few example-deps are:

  • exceljs
  • protobufjs
  • next.js
  • terser-webpack-plugin
  • uglify-js

There are many others, particularly code minifiers, TypeScript dependencies, and linting libraries. Has anyone suggestions on a more effective way to evaluate this?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Has anyone suggestions on a more effective way to evaluate this?

I'd probably set it to 'none', start the site and see where an error occurs.

exceljs

I doubt that we have this dependency in the site

Copy link
Contributor Author

@max-debug022 max-debug022 Sep 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd probably set it to 'none', start the site and see where an error occurs.

That's what I did in the first place, but the error messages are generic and rarely inform you about the specific package.

I doubt that we have this dependency in the site

Yes, sorry - I forgot to mention that I included all microservices since we not only use the CSP in the site, (although API wouldn't be necessary I guess).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can have a look at it together the next time we're both in the office.

site/next.config.js Outdated Show resolved Hide resolved
site/next.config.js Outdated Show resolved Hide resolved
site/next.config.js Outdated Show resolved Hide resolved
api/src/main.ts Outdated Show resolved Hide resolved
@max-debug022
Copy link
Contributor Author

max-debug022 commented Jul 8, 2024

Agreed. We could add line comments for each header/item.

@johnnyomair what headers should be documented and how? I don't think an explanation is useful since anyone can look headers up and they are fairly well explained. And if you understand the header you probably understand the logic behind their value.

For example. x-xss-protection - the first thing in the MDN docs is that it should not be used, so its value is false.

Edit: My initial documentation was somewhat like this:

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        "default-src": ["'none'"], // No external resources are needed
      },
      useDefaults: false, // Disable all other default CSP rules
    },
    xXssProtection: false, // Disable deprecated header
    xFrameOptions: false, // Disable deprecated header
    crossOriginResourcePolicy: "same-origin", // Do not allow cross-origin requests to access the response
    crossOriginEmbedderPolicy: true, // =no-corp Enable COEP
    crossOriginOpenerPolicy: true, // =same-origin Enable COOP
    strictTransportSecurity: {
      // Enable HSTS
      maxAge: 63072000, // 2 years (recommended if including subdomains)
      includeSubDomains: true, // Include all subdomains
      preload: true, // Enable HSTS preload
    },
    referrerPolicy: {
      policy: "no-referrer", // No referrer information needs to be sent
    },
    xContentTypeOptions: true, // = nosniff
    xDnsPrefetchControl: false, // Disable non-standard header as recommended by MDN
    xDownloadOptions: true, // = noopen (forces IE8+ not to open files in the context of the page but instead download them)
    xPermittedCrossDomainPolicies: true, // = none (prevent the browser from MIME-sniffing)
    originAgentCluster: true, // = false (disables the agent cluster)
  }),
);

@thomasdax98
Copy link
Member

Edit: My initial documentation was somewhat like this:

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        "default-src": ["'none'"], // No external resources are needed
      },
      useDefaults: false, // Disable all other default CSP rules
    },
    xXssProtection: false, // Disable deprecated header
    xFrameOptions: false, // Disable deprecated header
    crossOriginResourcePolicy: "same-origin", // Do not allow cross-origin requests to access the response
    crossOriginEmbedderPolicy: true, // =no-corp Enable COEP
    crossOriginOpenerPolicy: true, // =same-origin Enable COOP
    strictTransportSecurity: {
      // Enable HSTS
      maxAge: 63072000, // 2 years (recommended if including subdomains)
      includeSubDomains: true, // Include all subdomains
      preload: true, // Enable HSTS preload
    },
    referrerPolicy: {
      policy: "no-referrer", // No referrer information needs to be sent
    },
    xContentTypeOptions: true, // = nosniff
    xDnsPrefetchControl: false, // Disable non-standard header as recommended by MDN
    xDownloadOptions: true, // = noopen (forces IE8+ not to open files in the context of the page but instead download them)
    xPermittedCrossDomainPolicies: true, // = none (prevent the browser from MIME-sniffing)
    originAgentCluster: true, // = false (disables the agent cluster)
  }),
);

@max-debug022 I like this. During the review I had a hard time wrapping my head around "What does true (or false) mean in this case?"

I think you should add these comments

@max-debug022
Copy link
Contributor Author

I added the comments: cbc0472

thomasdax98
thomasdax98 previously approved these changes Jul 9, 2024
@johnnyomair
Copy link
Collaborator

@johnnyomair what headers should be documented and how? I don't think an explanation is useful since anyone can look headers up and they are fairly well explained. And if you understand the header you probably understand the logic behind their value.

I'd add comments for all settings that aren't obvious.

Also, I'd only change the headers if they differ from helmets default value.

site/next.config.js Outdated Show resolved Hide resolved
site/next.config.js Show resolved Hide resolved
johnnyomair
johnnyomair previously approved these changes Jul 22, 2024
@max-debug022
Copy link
Contributor Author

admin/server/server.js Outdated Show resolved Hide resolved
Copy link
Collaborator

@johnnyomair johnnyomair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix the lint workflow

@johnnyomair johnnyomair removed the request for review from mennoxx October 17, 2024 11:14
johnnyomair
johnnyomair previously approved these changes Oct 22, 2024
@johnnyomair
Copy link
Collaborator

@dkarnutsch @fraxachun @thomasdax98 please review, this one's been open for way too long 😁

site/next.config.js Show resolved Hide resolved
site/next.config.js Outdated Show resolved Hide resolved
site/next.config.js Outdated Show resolved Hide resolved
@@ -5,6 +5,31 @@ const withBundleAnalyzer = require("@next/bundle-analyzer")({
enabled: process.env.ANALYZE === "true",
});

function generateCSP() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

headers already is a function, so why not move the code there before returning the array?

cspRules["script-src"] = "'unsafe-eval'"; // Needed in local development
cspRules["connect-src"] = "ws:"; // Used for hot reloading in local development
} else {
cspRules["upgrade-insecure-requests"] = ""; // Don't use upgrade-insecure-requests with Domain-Setup
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need this at all?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This instructs user agents to treat a site's insecure URLs (HTTP) as though they have been replaced with secure URLs.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, but why do we need it? We don't serve any routes prefixed with http in our applications or do we?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Including upgrade-insecure-requests is optional but helps avoid potential misconfigurations and adds an extra layer of security, so I’d recommend keeping it.

@max-debug022
Copy link
Contributor Author

I will wait for this PR to be merged: #415

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants