A plugin which, when combined with HTMLWebpackPlugin, adds CSP tags to the HTML output
npm install @melloware/csp-webpack-plugininnerHTML calls to prevent XSS
shell
$ npm i --save-dev @melloware/csp-webpack-plugin
`
Basic Usage
Include the following in your webpack config:
`javascript
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CspHtmlWebpackPlugin = require('@melloware/csp-webpack-plugin');
module.exports = {
// rest of webpack config
plugins: [
new HtmlWebpackPlugin()
new CspHtmlWebpackPlugin({
// config here, see below
})
]
}
`
Recommended Configuration
By default, the @melloware/csp-webpack-plugin has a very lax policy. You should configure it for your needs.
A good starting policy would be the following:
`javascript
new CspHtmlWebpackPlugin({
'script-src': '',
'style-src': ''
});
`
Although we're configuring script-src and style-src to be blank, the CSP plugin will scan your HTML
generated in html-webpack-plugin for external/inline script and style tags, and will add the appropriate
hashes and nonces to your CSP policy. This configuration will also add a base-uri and object-src entry
that exist in the default policy:
`xml
`
This configuration should work for most use cases, and will provide a strong layer of extra security.
All Configuration Options
$3
This CspHtmlWebpackPlugin accepts 2 params with the following structure:
- {object} Policy (optional) - a flat object which defines your CSP policy. Valid keys and values can be found on the MDN CSP page. Values can either be a string, or an array of strings.
- {object} Additional Options (optional) - a flat object with the optional configuration options:
- {boolean|Function} enabled - if false, or the function returns false, the empty CSP tag will be stripped from the html output.
- The htmlPluginData is passed into the function as it's first param.
- If enabled is set the false, it will disable generating a CSP for all instances of HtmlWebpackPlugin in your webpack config.
- {boolean} integrityEnabled - Enable or disable SHA384 Subresource Integrity
- {boolean} primeReactEnabled - Enable or disable custom PrimeReact NONCE value added to the environment for inline styles.
- {boolean} trustedTypesEnabled - Enable or disable Trusted Types handling which automatically adds DOMPurify to sanitize innerHTML calls to prevent XSS
- {string} hashingMethod - accepts 'sha256', 'sha384', 'sha512' - your node version must also accept this hashing method.
- {object} hashEnabled - a entry for which policy rules are allowed to include hashes
- {object} nonceEnabled - a entry for which policy rules are allowed to include nonces
- {Function} processFn - allows the developer to overwrite the default method of what happens to the CSP after it has been created
- Parameters are:
- builtPolicy: a string containing the completed policy;
- htmlPluginData: the HtmlWebpackPlugin object;
- $: the cheerio object of the html file currently being processed
- compilation: Internal webpack object to manipulate the build
Trusted Types
Trusted Types is a newer CSP directive which adds XSS protection by preventing innerHTML without being trusted.
To add Trusted Type support automatically to your application you would add the require-trusted-types-for 'script' CSP directive.
`javascript
{
'base-uri': "'self'",
'object-src': "'none'",
'script-src': ["'strict-dynamic'"],
'style-src': ["'self'"],
'require-trusted-types-for': ["'script'"]
};
`
If trustedTypesEnabled=true this plugin will automatically add a special script which executes before any other script to enable a default policy that sanitizes HTML using DOMPurify.
`javascript
import DOMPurify from 'dompurify';
if (window.trustedTypes && window.trustedTypes.createPolicy) { // Feature testing
window.trustedTypes.createPolicy('default', {
createHTML: (string) => DOMPurify.sanitize(string, {RETURN_TRUSTED_TYPE: true}),
createScriptURL: string => sanitizeUrl(string),
createScript: string => string // allow scripts
});
};
`
You will need to include DOMPurify and Trusted Types Polyfill using npm install dompurify trusted-types to your package.json.
Appendix
#### Default Policy:
`javascript
{
'base-uri': "'self'",
'object-src': "'none'",
'script-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"],
'style-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"]
};
`
#### Default Additional Options:
`javascript
{
enabled: true,
integrityEnabled: true,
primeReactEnabled: true,
trustedTypesEnabled: true,
hashingMethod: 'sha384',
hashEnabled: {
'script-src': true,
'style-src': true
},
nonceEnabled: {
'script-src': true,
'style-src': true
},
processFn: defaultProcessFn
}
`
#### Full Default Configuration:
`javascript
new CspHtmlWebpackPlugin({
'base-uri': "'self'",
'object-src': "'none'",
'script-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"],
'style-src': ["'unsafe-inline'", "'self'", "'unsafe-eval'"]
}, {
enabled: true,
integrityEnabled: true,
primeReactEnabled: true,
trustedTypesEnabled: true,
hashingMethod: 'sha384',
hashEnabled: {
'script-src': true,
'style-src': true
},
nonceEnabled: {
'script-src': true,
'style-src': true
},
processFn: defaultProcessFn // defined in the plugin itself
})
`
Advanced Usage
$3
Some specific directives require the CSP to be sent to the client via a response header (e.g. report-uri and report-to)
You can set your own processFn callback to make this happen.
#### nginx
In your webpack config:
`javascript
const RawSource = require('webpack-sources').RawSource;
function generateNginxHeaderFile(
builtPolicy,
_htmlPluginData,
_obj,
compilation
) {
const header =
'add_header Content-Security-Policy "' +
builtPolicy +
'; report-uri /csp-report/ ";';
compilation.emitAsset('nginx-csp-header.conf', new RawSource(header));
}
module.exports = {
{...},
plugins: [
new CspHtmlWebpackPlugin(
{...}, {
processFn: generateNginxHeaderFile
})
]
};
`
In your nginx config:
`nginx
location / {
...
include /path/to/webpack/output/nginx-csp-header.conf
}
`
Publishing
Adjust the version in the package.json if necessary, then
`shell
npm login
This will run npm run build automatically
npm publish --access public
``