Skip to content

Latest commit

 

History

History
529 lines (400 loc) · 12.2 KB

File metadata and controls

529 lines (400 loc) · 12.2 KB

Migration Guide - From Insecure to Secure JavaScript Patterns

This guide helps you migrate existing insecure JavaScript code to secure patterns step-by-step.

Table of Contents


Before You Start

Prerequisites

  1. Backup your codebase
  2. Set up version control (git)
  3. Install Node.js 14+ for testing
  4. Review OWASP Top 10 vulnerabilities

Risk Assessment

Prioritize migration based on:

  • Critical: User authentication, payment processing, admin functions
  • High: User profile management, data display, file uploads
  • Medium: Read-only pages, static content with user input
  • Low: Internal tools, logged actions only

Input Validation Migration

Step 1: Identify Vulnerable Patterns

Search your codebase for:

// ❌ Insecure patterns to find
function processUser(data) {
    return data; // No validation
}

app.post('/api/user', (req, res) => {
    const userData = req.body; // Direct use without validation
});

Step 2: Replace with Secure Validation

// ✅ Secure replacement
const { validateInput } = require('./security-patterns/input-validation');

function processUser(data) {
    const validation = validateInput(data.username, {
        type: 'string',
        minLength: 3,
        maxLength: 50,
        pattern: /^[a-zA-Z0-9_]+$/
    });
    
    if (!validation.isValid) {
        throw new Error(`Invalid username: ${validation.errors.join(', ')}`);
    }
    
    return validation.sanitized;
}

app.post('/api/user', (req, res) => {
    try {
        const safeUsername = processUser(req.body);
        // Proceed with safe data
        res.json({ success: true });
    } catch (error) {
        res.status(400).json({ error: error.message });
    }
});

Step 3: Add Type Checking

// Before: No type checking
function calculateTotal(price, quantity) {
    return price * quantity;
}

// After: Strict type validation
function calculateTotal(price, quantity) {
    if (typeof price !== 'number' || typeof quantity !== 'number') {
        throw new TypeError('Price and quantity must be numbers');
    }
    
    if (price < 0 || quantity < 0) {
        throw new RangeError('Values must be positive');
    }
    
    if (!Number.isFinite(price) || !Number.isFinite(quantity)) {
        throw new Error('Values must be finite numbers');
    }
    
    return price * quantity;
}

Common Validation Patterns

// Email validation
const emailValidation = validateInput(email, {
    type: 'email',
    maxLength: 254
});

// URL validation
const urlValidation = validateInput(url, {
    type: 'url',
    allowedProtocols: ['https']
});

// Phone validation
const phoneValidation = validateInput(phone, {
    type: 'string',
    pattern: /^\+?[1-9]\d{1,14}$/
});

// Date validation
const dateValidation = validateInput(dateStr, {
    type: 'date',
    minDate: '2020-01-01',
    maxDate: '2030-12-31'
});

XSS Prevention Migration

Step 1: Find Vulnerable DOM Manipulations

# Search for dangerous patterns
grep -r "innerHTML" src/
grep -r "document.write" src/
grep -r "eval" src/

Step 2: Replace innerHTML with Secure Alternatives

// ❌ Before: Direct innerHTML
element.innerHTML = userContent;

// ✅ After: Sanitized content
const { sanitizeHTML } = require('./security-patterns/xss-prevention');
element.innerHTML = sanitizeHTML(userContent, {
    allowBasicFormatting: true,
    allowLinks: true
});

Step 3: Migrate Dynamic HTML Generation

// ❌ Before: String concatenation
function renderComment(comment) {
    return `
        <div class="comment">
            <h3>${comment.author}</h3>
            <p>${comment.text}</p>
            <a href="${comment.website}">Website</a>
        </div>
    `;
}

// ✅ After: Escaped and validated
const { escapeHTML, validateURL } = require('./security-patterns/xss-prevention');

function renderComment(comment) {
    const urlValidation = validateURL(comment.website);
    const safeURL = urlValidation.isValid ? urlValidation.sanitizedURL : '#';
    
    return `
        <div class="comment">
            <h3>${escapeHTML(comment.author)}</h3>
            <p>${escapeHTML(comment.text)}</p>
            <a href="${safeURL}">Website</a>
        </div>
    `;
}

Step 4: Secure Event Handlers

// ❌ Before: Inline event handlers
button.setAttribute('onclick', userProvidedCode);

// ✅ After: addEventListener
button.addEventListener('click', () => {
    // Controlled code execution
    handleUserAction();
});

Step 5: Implement Content Security Policy

// Express.js example
const { CSP } = require('./security-patterns/xss-prevention');

app.use((req, res, next) => {
    CSP.setHeader(res, {
        allowInlineStyles: false,
        allowInlineScripts: false,
        allowEval: false,
        trustedDomains: ['https://cdn.example.com']
    });
    next();
});

Eval Alternatives Migration

Step 1: Replace eval() for Calculations

// ❌ Before: Dangerous eval
function calculate(expression) {
    return eval(expression);
}

// ✅ After: Secure calculator
const { safeCalculator } = require('./security-patterns/eval-alternatives');

function calculate(expression) {
    const result = safeCalculator(expression);
    
    if (result.error) {
        throw new Error(result.error);
    }
    
    return result.result;
}

Step 2: Replace Dynamic Property Access

// ❌ Before: eval-based property access
function getNestedValue(obj, path) {
    return eval(`obj.${path}`);
}

// ✅ After: Safe property access
const { safeJSONPath } = require('./security-patterns/eval-alternatives');

function getNestedValue(obj, path) {
    const result = safeJSONPath(obj, path);
    
    if (!result.success) {
        return undefined;
    }
    
    return result.value;
}

Step 3: Secure Template Processing

// ❌ Before: eval in templates
function processTemplate(template, data) {
    return template.replace(/\{\{(.+?)\}\}/g, (match, code) => {
        return eval(code);
    });
}

// ✅ After: Safe template
const { safeTemplate } = require('./security-patterns/eval-alternatives');

function processTemplate(template, data) {
    return safeTemplate(template, data);
}

Step 4: Replace setTimeout/setInterval with Strings

// ❌ Before: String-based timeout
setTimeout("doSomething()", 1000);

// ✅ After: Function-based timeout
setTimeout(() => doSomething(), 1000);

Step 5: Secure Configuration Parsing

// ❌ Before: eval-based config
function parseConfig(configString) {
    return eval(`(${configString})`);
}

// ✅ After: Safe JSON parsing
const { safeJSONParse } = require('./security-patterns/xss-prevention');

function parseConfig(configString) {
    const result = safeJSONParse(configString);
    
    if (!result.success) {
        throw new Error(`Config parse error: ${result.error}`);
    }
    
    return result.data;
}

Testing After Migration

Automated Security Tests

// test/security-regression.js
const assert = require('assert');
const { sanitizeHTML, validateInput, secureEval } = require('../index');

describe('Security Regression Tests', () => {
    it('should block script injection', () => {
        const result = sanitizeHTML('<script>alert("XSS")</script>');
        assert(!result.includes('<script>'), 'Script tags should be removed');
    });
    
    it('should reject invalid input', () => {
        const result = validateInput('<script>', { type: 'string', maxLength: 100 });
        assert(!result.isValid, 'Should reject malicious input');
    });
    
    it('should prevent code injection in eval', () => {
        const result = secureEval('alert("XSS")');
        assert(!result.success, 'Should block dangerous code');
    });
});

Manual Testing Checklist

  • Test with malicious payloads from OWASP XSS cheat sheet
  • Verify error messages don't leak sensitive information
  • Test with various character encodings (UTF-8, UTF-16)
  • Check performance with large inputs (DoS prevention)
  • Validate CORS and CSP headers are set correctly
  • Test on multiple browsers (Chrome, Firefox, Safari, Edge)

Penetration Testing

Use tools like:

  • OWASP ZAP: Automated security scanner
  • Burp Suite: Manual testing and fuzzing
  • npm audit: Check dependencies for vulnerabilities
# Run security audit
npm audit
npm audit fix

# Check for known vulnerabilities
npx snyk test

Performance Considerations

Benchmark Before and After

const { performance } = require('perf_hooks');

// Benchmark insecure version
const start1 = performance.now();
for (let i = 0; i < 10000; i++) {
    insecureFunction(testData);
}
const insecureTime = performance.now() - start1;

// Benchmark secure version
const start2 = performance.now();
for (let i = 0; i < 10000; i++) {
    secureFunction(testData);
}
const secureTime = performance.now() - start2;

console.log(`Overhead: ${((secureTime - insecureTime) / insecureTime * 100).toFixed(2)}%`);

Expected Overhead

Pattern Overhead Acceptable For
Input Validation 0.1-0.5ms All use cases
HTML Sanitization 0.2-1ms User-generated content
Secure Eval 0.05-0.2ms Configuration, calculators
URL Validation 0.1-0.3ms Link processing

Optimization Tips

  1. Cache validation results for repeated inputs
  2. Validate once at entry points rather than everywhere
  3. Use lazy validation for non-critical paths
  4. Batch operations when processing multiple items
// Example: Cached validation
const validationCache = new Map();

function validateWithCache(input, rules) {
    const cacheKey = `${input}_${JSON.stringify(rules)}`;
    
    if (validationCache.has(cacheKey)) {
        return validationCache.get(cacheKey);
    }
    
    const result = validateInput(input, rules);
    validationCache.set(cacheKey, result);
    
    return result;
}

Common Migration Pitfalls

1. Over-Sanitization

// ❌ Too aggressive
function displayUsername(name) {
    return sanitizeHTML(name); // Removes all formatting
}

// ✅ Appropriate sanitization
function displayUsername(name) {
    return escapeHTML(name); // Just escapes entities
}

2. Incomplete Validation

// ❌ Only client-side validation
// Client: validates email format
// Server: trusts client input ← VULNERABLE

// ✅ Always validate server-side
// Client: validates for UX
// Server: validates for security ← SECURE

3. Ignoring Edge Cases

Test with:

  • Empty strings
  • Null/undefined
  • Very long inputs (> 10MB)
  • Unicode characters
  • Control characters
  • Encoded payloads

Rollback Plan

If issues arise after migration:

  1. Keep old code in git (don't delete, comment out)
  2. Deploy to staging first
  3. Use feature flags for gradual rollout
  4. Monitor error rates closely
  5. Have rollback script ready
// Feature flag example
const USE_SECURE_VALIDATION = process.env.SECURE_MODE === 'true';

function processInput(data) {
    if (USE_SECURE_VALIDATION) {
        return secureValidate(data);
    } else {
        return legacyValidate(data);
    }
}

Getting Help


Checklist: Migration Complete

  • All user inputs validated
  • innerHTML replaced with sanitized alternatives
  • eval() removed or replaced with secure alternatives
  • Event handlers use addEventListener
  • CSP headers implemented
  • Automated security tests passing
  • Performance benchmarks acceptable
  • Code review completed
  • Staging deployment successful
  • Production monitoring in place

Last updated: September 30, 2025
Maintained by: Nexus Studio