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();
});