We're sorry but this app doesn't work properly without JavaScript enabled. Please enable it to continue.

This lesson's interactive features are locked, please to keep using them

Helmet

Security headers let a server tell the browser which behavior to permit, but they're easy to get subtly wrong. The helmet middleware centralizes security-header configuration for Express:

import helmet from "helmet";

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        scriptSrc: [
          // ...
        ],
        styleSrc: [
          // ...
        ],
        frameSrc: [
          // ...
        ],
        // etc.
      },
    },
    referrerPolicy: { policy: "strict-origin-when-cross-origin" },
    // etc.
  }),
);

Helmet's defaults are a decent starting point, but they're not a perfect drop-in for every app. Its broader baseline includes Cross-Origin-Opener-Policy, Cross-Origin-Resource-Policy, and Origin-Agent-Cluster. Keep those defaults where they fit, but configure each policy around the resources and browser behavior your app actually needs.

Tune the Defaults

Helmet's default CSP already sets a sensible baseline:

  • default-src 'self'
  • base-uri 'self'
  • form-action 'self'
  • frame-ancestors 'self'
  • img-src 'self' data:
  • object-src 'none'.

So, rather than replacing the whole policy, override only the directives where your app differs. Common adjustments are:

  • The default style-src allows 'unsafe-inline'. Most apps should tighten it to 'self'.
  • The default script-src has no nonce. Helmet accepts a function for per-response values:
    scriptSrc: ["'self'", (_req, res) => `'nonce-${String((res as Response).locals.cspNonce)}'`],
    

    Helmet types that callback with Node's ServerResponse, which has no locals, hence the cast to Express's Response. The nonce itself still has to be generated by an earlier middleware and stored on res.locals, because the Helmet config is built once at startup, not per request.

  • There's no default frame-src, so an app that embeds iframes needs to add one.
  • The defaults include upgrade-insecure-requests, which doesn't belong on a local HTTP app. Setting a directive to null removes it.

Further, a sandboxed iframe without allow-same-origin has an opaque origin. That means Helmet's default Cross-Origin-Resource-Policy: same-origin blocks even stylesheet and script URLs hosted by the parent app when the sandboxed frame loads them. If a frame needs those resources, override the header only on those specific public assets.

Helmet also defaults to a different referrer policy, so you should set strict-origin-when-cross-origin explicitly. And for a local HTTP app, disable Strict-Transport-Security. Transport enforcement only belongs in a production environment that's actually set up for HTTPS.

Assignment

Bearly Secure configures its browser security headers by hand. Adopt Helmet's default baseline, overriding only the policies where Bearly Secure differs from it.

With Bearly Secure still running, run and submit the CLI tests from the project root.