Content Security Policy helps prevent unwanted content being injected into your webpages; this can mitigate XSS vulnerabilities, unintended frames, malicious frames, and more. If you want to learn how CSP works, check out the fantastic HTML5 Rocks guide, the Content Security Policy Reference, and the Content Security Policy specification. This module helps set Content Security Policies.
Usage:
var csp = require('helmet-csp')
app.use(csp({
// Specify directives as normal.
directives: {
defaultSrc: ["'self'", 'default.com'],
scriptSrc: ["'self'", "'unsafe-inline'"],
styleSrc: ['style.com'],
imgSrc: ['img.com', 'data:'],
sandbox: ['allow-forms', 'allow-scripts'],
reportUri: '/report-violation'
objectSrc: [], // An empty array allows nothing through
}
// Set to true if you only want browsers to report errors, not block them
reportOnly: false,
// Set to true if you want to blindly set all headers: Content-Security-Policy,
// X-WebKit-CSP, and X-Content-Security-Policy.
setAllHeaders: false,
// Set to true if you want to disable CSP on Android where it can be buggy.
disableAndroid: false
}))
There are a lot of inconsistencies in how browsers implement CSP. Helmet sniffs the user-agent of the browser and sets the appropriate header and value for that browser. If no user-agent is matched, it will set all the headers with the 2.0 spec.
If you've specified a reportUri
, browsers will POST any CSP violations to your server. Here's a simple example of a route that handles those reports:
// You need a JSON parser first.
app.use(bodyParser.json({
type: ['json', 'application/csp-report']
}))
app.post('/report-violation', function (req, res) {
if (req.body) {
console.log('CSP Violation: ', req.body)
} else {
console.log('CSP Violation: No data received!')
}
res.status(204).end()
})
Not all browsers send CSP violations in the same way, so this might require a little work.
Note: If you're using a CSRF module like csurf, you might have problems handling these violations without a valid CSRF token. The fix is to put your CSP report route above csurf middleware.
You can dynamically generate nonces to allow inline <script>
tags to be safely evaluated. Here's a simple example:
var uuid = require('node-uuid')
app.use(function (req, res, next) {
res.locals.nonce = uuid.v4()
next()
})
app.use(csp({
directives: {
scriptSrc: [
"'self'",
function (req, res) {
return "'nonce-" + res.locals.nonce + "'" // 'nonce-614d9122-d5b0-4760-aecf-3a5d17cf0ac9'
}
]
}
}))
app.use(function (req, res) {
res.end('<script nonce="' + res.locals.nonce + '">alert(1 + 1);</script>')
})