slackhq / csp-html-webpack-plugin

A plugin which, when combined with HTMLWebpackPlugin, adds CSP tags to the HTML output.
MIT License
164 stars 40 forks source link

Documentation may be misleading and hashEnabled option does not work #57

Closed dvlden closed 3 years ago

dvlden commented 4 years ago

Description

Straight from the docs, we can see this:

new HtmlWebpackPlugin({
  cspPlugin: {
    enabled: true,
    policy: {
      'base-uri': "'self'",
      'object-src': "'none'",
      'script-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"],
      'style-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"]
    },
    hashEnabled: {
      'script-src': true,
      'style-src': true
    },
    nonceEnabled: {
      'script-src': true,
      'style-src': true
    }
  }
});

new CspHtmlWebpackPlugin({
  'base-uri': "'self'",
  'object-src': "'none'",
  'script-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"],
  'style-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"]
}, {
  enabled: true,
  hashingMethod: 'sha256',
  hashEnabled: {
    'script-src': true,
    'style-src': true
  },
  nonceEnabled: {
    'script-src': true,
    'style-src': true
  }
})

It tells us about the default options, but it fails to tell us that it can be used one way or another. If that's not the case, well, it works that way for me. I was looking at the docs for the first time and immediately thought that configuration must be placed within both places, which is weird. But that doesn't seem to be the case. We don't have to define cspPolicy property within HTMLWebpackPlugin if we are using CspHtmlWebpackPlugin.

Anyways, aside from this, I came across a real issue. The option hashEnabled does not seem to be working. If I set both properties to true or to false, it does nothing... No hash calculations of internal scripts and styles, so they are nowhere to be found within CSP tag.

However, if I only enable nonceEnabled it seems to be working, not quite sure YET, but I can see nonce hashes in the CSP tag. Howevever, nonce should be different for each page load, so that's not really what I'm looking for in my SPA.

What type of issue is this? (place an x in one of the [ ])

Requirements (place an x in each of the [ ])


Bug Report

I assume it's a bug, but it might be a misunderstanding of usage. hashEnabled – not working.

Reproducible in:

slackhq/csp-html-webpack-plugin version: ^3.0.4

node version: v13.11.0

OS version(s): macOS Catalina 10.15.3 (19D76)

Steps to reproduce:

  1. Use vue-json-pretty npm package, as it will inject style on each load. Any plugin will do, but this is an example. You might also make a small script that will inject some CSS.

Expected result:

To have SHA-256 hashes within my CSP policy for internal scripts and styles.

Actual result:

Nothing. It literally does nothing.

AnujRNair commented 4 years ago

Hi @dvlden

That's a fair point about the documentation, I can have that fixed up.

When it comes to the hashes not being added as expected, are you able to provide a small reproducible example repo where I could debug this?

As a side note, all scripts must exist on the page at the point this plugin starts executing, as it will only calculate hashes for script found during webpack compile time. Anything else will have to be manually calculated and added into the CSP plugin settings manually

dvlden commented 4 years ago

I think the simplest solution for me here is to get it out of the stash, make a new Vue project and take only bits for this specific problem.

Well, I'd assume that the plugin has CSS within the JS module since it's being injected internally and there's no way to import the style externally.

The scenario is as follow:

That being said, I think that this plugin should pick that up, right?

AnujRNair commented 4 years ago

It depends on how the CSS is being injected into the head of the doc. If it's being injected during runtime, then your CSP is probably blocking the script from running.

This is the correct behaviour.

Browsers cannot know whether a script being injected into the document has come from a legitimate source, or from an attacker. You have to let the browser know via the CSP that what is trying to run has been verified by you, and the only way to do this is to precompute the script into a hash and include it in the policy.

This plugin works by scanning the HTML file during webpack's compile time to find all inline script and style tags and precompute the hashes for you. If there are hashes outside of the HTML file which need to be whitelisted to allow them to run during runtime, you will need to manually compute these and provide them to the CspHtmlWebpackPlugin policy yourself

dvlden commented 4 years ago

Aha... Well, I thought that the plugin will recognize the CSS from the JS library that injects it, since it's visible within the module itself. But seems like it's not doing that... Yeah, module injects style during runtime.

Could this be improved so that the plugin automatically recognizes when the JS module has some CSS to inject and to calculate the SHA hash and set it as the directive?

AnujRNair commented 4 years ago

This would be unmanageable to auto calculate and inject in since all libraries will do this in different ways, at different points in their runtime, and through different methods. The webpack plugin would have to understand runtime code at a very deep level to be able to do this, which is out of scope for the project. On top of this, any post processing done through webpack (compression, minification etc.) would render the hash initially created obsolete.

I would recommend either extracting your CSS out to a separate file, or turning off CSP rules which stop inline CSS from being added.

madhukivera commented 9 months ago

To anyone who is coming here and wondering how to extract css in webpack. Remove style-loader first(it is meant to be used in development only) and use this plugin https://webpack.js.org/loaders/style-loader/#recommend