Implementing Nonces and Hashes
Implementing Nonces and Hashes
Moving away from 'unsafe-inline'
to nonces or hashes significantly improves security. Here's how to implement these mechanisms effectively:
// Nonce Implementation System
class NonceManager {
generateNonce() {
return crypto.randomBytes(16).toString('base64');
}
applyNoncesToHTML(html, nonce) {
// Add nonce to inline scripts
html = html.replace(
/<script(?![^>]*\snonce=)([^>]*)>/gi,
`<script nonce="${nonce}"$1>`
);
// Add nonce to inline styles
html = html.replace(
/<style(?![^>]*\snonce=)([^>]*)>/gi,
`<style nonce="${nonce}"$1>`
);
return html;
}
extractInlineContent(html) {
const scripts = [];
const styles = [];
// Extract inline scripts
const scriptRegex = /<script[^>]*>([^<]+)<\/script>/gi;
let match;
while ((match = scriptRegex.exec(html)) !== null) {
scripts.push(match[1].trim());
}
// Extract inline styles
const styleRegex = /<style[^>]*>([^<]+)<\/style>/gi;
while ((match = styleRegex.exec(html)) !== null) {
styles.push(match[1].trim());
}
return { scripts, styles };
}
calculateHashes(content) {
const encoder = new TextEncoder();
const data = encoder.encode(content);
return crypto.subtle.digest('SHA-256', data)
.then(buffer => {
const base64 = btoa(String.fromCharCode(...new Uint8Array(buffer)));
return `'sha256-${base64}'`;
});
}
}
// Template engine integration (EJS example)
app.set('view engine', 'ejs');
app.use(async (req, res, next) => {
const nonceManager = new NonceManager();
const nonce = nonceManager.generateNonce();
// Override render method
const originalRender = res.render;
res.render = function(view, options, callback) {
options = options || {};
options.cspNonce = nonce;
// Set CSP header with nonce
res.setHeader('Content-Security-Policy',
`default-src 'self'; ` +
`script-src 'self' 'nonce-${nonce}' https://cdnjs.cloudflare.com; ` +
`style-src 'self' 'nonce-${nonce}' https://fonts.googleapis.com`
);
originalRender.call(this, view, options, callback);
};
next();
});