Injection Attacks in JavaScript

Injection Attacks in JavaScript

While SQL injection is well-known, JavaScript applications face various injection attacks including NoSQL injection, command injection in Node.js, and template injection. These vulnerabilities occur when user input is improperly incorporated into database queries, system commands, or template engines.

// NoSQL Injection Prevention

// VULNERABLE: MongoDB query with user input
async function vulnerableLogin(username, password) {
    // This allows injection like: username = {$ne: null}
    const user = await db.collection('users').findOne({
        username: username,
        password: password
    });
    return user;
}

// SECURE: Validate and sanitize inputs
async function secureLogin(username, password) {
    // Type validation
    if (typeof username !== 'string' || typeof password !== 'string') {
        throw new Error('Invalid input types');
    }
    
    // Length and character validation
    if (username.length > 50 || !/^[a-zA-Z0-9_-]+$/.test(username)) {
        throw new Error('Invalid username format');
    }
    
    // Use parameterized queries where possible
    const user = await db.collection('users').findOne({
        username: { $eq: username },  // Explicit equality
        password: { $eq: hashPassword(password) }
    });
    
    return user;
}

// Command Injection Prevention in Node.js
const { spawn } = require('child_process');
const path = require('path');

// VULNERABLE: Command injection
function vulnerableImageResize(userFilename, size) {
    // This allows injection like: userFilename = "image.jpg; rm -rf /"
    const command = `convert ${userFilename} -resize ${size} output.jpg`;
    require('child_process').exec(command); // DANGEROUS!
}

// SECURE: Use spawn with argument array
function secureImageResize(userFilename, size) {
    // Validate inputs
    if (!/^[a-zA-Z0-9_-]+\.(jpg|png|gif)$/i.test(userFilename)) {
        throw new Error('Invalid filename');
    }
    
    if (!/^\d+x\d+$/.test(size)) {
        throw new Error('Invalid size format');
    }
    
    // Ensure file exists and is in allowed directory
    const safePath = path.join(__dirname, 'uploads', userFilename);
    if (!safePath.startsWith(path.join(__dirname, 'uploads'))) {
        throw new Error('Path traversal attempt');
    }
    
    // Use spawn with separate arguments (no shell)
    const convert = spawn('convert', [
        safePath,
        '-resize', size,
        path.join(__dirname, 'output', 'resized-' + userFilename)
    ], {
        timeout: 30000, // 30 second timeout
        cwd: __dirname,
        env: {} // Minimal environment
    });
    
    return new Promise((resolve, reject) => {
        convert.on('exit', (code) => {
            if (code === 0) resolve();
            else reject(new Error(`Process exited with code ${code}`));
        });
    });
}

// Template Injection Prevention
// VULNERABLE: User input in template string
function vulnerableTemplate(userInput) {
    // This allows code execution!
    return eval(`\`Hello ${userInput}\``);
}

// SECURE: Safe template rendering
function secureTemplate(data) {
    // Use a proper template engine with auto-escaping
    const Handlebars = require('handlebars');
    
    // Compile template
    const template = Handlebars.compile('Hello {{name}}');
    
    // Data is automatically escaped
    return template({ name: data.userInput });
}

// For dynamic templates, use sandboxing
const vm = require('vm');

function sandboxedTemplate(templateStr, context) {
    // Create a limited context
    const sandbox = {
        data: context,
        console: { log: () => {} }, // Limited console
        // Don't expose dangerous globals
    };
    
    // Run in sandbox with timeout
    try {
        const script = new vm.Script(`
            // Limit template processing
            const result = data.name ? \`Hello \${data.name}\` : 'Hello Guest';
            result;
        `);
        
        return script.runInNewContext(sandbox, {
            timeout: 100, // 100ms timeout
            displayErrors: false
        });
    } catch (error) {
        throw new Error('Template processing failed');
    }
}

Understanding JavaScript vulnerabilities is crucial for building secure applications. The dynamic nature of JavaScript, combined with its execution in untrusted environments, creates unique security challenges. By recognizing these vulnerabilities and implementing proper defenses, developers can significantly improve their applications' security posture.## Secure Coding Standards

Establishing and following secure coding standards is fundamental to building resilient applications that can withstand modern security threats. These standards provide developers with clear guidelines on how to write code that is not only functional but also secure by design. This chapter presents comprehensive secure coding standards for both Python and JavaScript, covering everything from naming conventions and code organization to specific security patterns and anti-patterns that every developer should know.