nibtime / next-safe-middleware

Strict CSP (Content-Security-Policy) for Next.js hybrid apps https://web.dev/strict-csp/
https://next-safe-middleware.vercel.app
MIT License
78 stars 20 forks source link

Remove Unnecessary 'unsafe-hashes' from style-src in CSP #100

Closed lanzosuarez closed 8 months ago

lanzosuarez commented 8 months ago

I've noticed that the library is currently adding 'unsafe-hashes' to the style-src directive in the Content Security Policy (CSP). As per the CSP standard, 'unsafe-hashes' is intended for use with script-src to allow specific inline scripts, and its use within style-src is non-standard and unnecessary. This inclusion could potentially lead to confusion and might not align with best practices for CSP implementation.

Steps to Reproduce

  1. Use strictInlineStyles in the security middleware inside middleware.ts import { chainMatch, isPageRequest, csp, strictDynamic, strictInlineStyles, } from "@next-safe/middleware";

    const securityMiddleware = [
    ...,
    strictDynamic(),
    strictInlineStyles(),
    ];
    export default chainMatch(isPageRequest)(...securityMiddleware);
  2. Pass trustifyStyles: true in the getCspInitialProps call inside document.ts

    const initialProps = await getCspInitialProps({
    ctx,
    trustifyStyles: true,
    });
  3. Observe the generated CSP header.

  4. Note the inclusion of 'unsafe-hashes' in the style-src directive.

Expected Behavior

The style-src directive should not include 'unsafe-hashes', as this is not a standard practice for styling and it does not enhance security for styles.

Actual Behavior

The CSP header generated by the library includes 'unsafe-hashes' in the style-src directive.

Possible Solution

In builder.ts inside withStyleHashes, remove the addition of the [unsafe-hashes] in the style-src directive

 public withStyleHashes(
    elemHashes: HashWithAlgorithm[] = [],
    attrHashes: HashWithAlgorithm[] = [],
    removeUnsafeInline = true
  ) {
    const unsafeHashes = attrHashes.length ? ["unsafe-hashes"] : []; // remove this line
    if (elemHashes.length || attrHashes.length) {
      this.withDirectives({
        "style-src": [...elemHashes, ...attrHashes, ...unsafeHashes], // remove the ...unsafeHashes part
      });
    }
    if (this.hasDirective("style-src-elem") && elemHashes.length) {
      this.withDirectives({
        "style-src-elem": [...elemHashes],
      });
    }
    if (this.hasDirective("style-src-attr") && attrHashes.length) {
      this.withDirectives({
        "style-src-attr": [...attrHashes, ...unsafeHashes],  // remove the ...unsafeHashes part
      });
    }
    if (removeUnsafeInline) {
      this.withoutDirectiveValues({
        "style-src": ["unsafe-inline"],
        "style-src-elem": ["unsafe-inline"],
        "style-src-attr": ["unsafe-inline"],
      });
    }
    return this;
  }
lanzosuarez commented 8 months ago

Turns out the unsafe-hashes is needed in Chrome for the hashes to be "Matched"

Csp spec Same SO issue