Introduction to Injection Flaws

Welcome back, future security champions! In our previous chapters, we laid the groundwork for thinking like an attacker and understanding the core principles of web application security. Now, we’re diving into one of the most pervasive and dangerous vulnerabilities on the internet: Injection Flaws. This category frequently sits at or near the top of the OWASP Top 10 list, highlighting its critical importance.

What exactly is an Injection Flaw? Imagine you’re sending a message, but someone slips in extra instructions that the recipient then accidentally executes as part of their own duties. That’s the essence of injection. It occurs when untrusted data is sent to an interpreter as part of a command or query. The attacker’s hostile data can trick the interpreter into executing unintended commands or accessing data without proper authorization. This chapter will focus on three common types: SQL Injection, NoSQL Injection, and Command Injection.

By the end of this chapter, you’ll not only understand how these attacks work internally but also how to safely reproduce them in a controlled environment and, most importantly, how to build robust defenses against them using modern best practices as of January 2026. Get ready to put on your detective hat and strengthen your web development skills!

Core Concepts: Understanding Injection

Injection is fundamentally about confusing a system. It’s when data you think is just data gets treated as code or commands. This can lead to disastrous consequences, from data breaches to complete server takeover.

2.1 What is Injection? The Fundamental Idea

At its heart, an injection flaw exploits the boundary between data and code. Many applications construct commands or queries by concatenating (joining) user-supplied input with internal code. If this input isn’t properly handled, a malicious user can inject their own code into the mix, altering the original command’s intent.

Think of it like this: You ask a librarian (your application’s backend) for a book by title. If you just give them the title, everything’s fine. But what if you give them the title and slip in a note that says, “Also, while you’re at it, please bring me all the library’s financial records”? If the librarian isn’t trained to distinguish your legitimate request from the sneaky extra command, they might just follow both!

Let’s visualize this flow:

flowchart TD A[User Input] --> B{Application Backend} B -->|Constructs Query/Command| C[Interpreter] C --> D[Execution] D --> E[Result] subgraph Injection Attack F[Malicious User Input] --> B B -->|Injects Malicious Code| C C -->|Unintended Execution| G[Attack Success!] end

The key takeaway here is that the Interpreter (be it a database server, an operating system shell, or another component) cannot differentiate between the application’s intended code and the malicious code injected by the user if the input isn’t properly isolated.

2.2 SQL Injection (SQLi)

SQL Injection is perhaps the most well-known type of injection. It targets SQL databases, allowing attackers to interfere with the queries an application makes to its database.

What it is:

SQL Injection (SQLi) occurs when an attacker can manipulate or inject malicious SQL code into input fields or parameters, which are then included in an SQL query executed by the database.

How it works:

Consider an application that uses user input to build an SQL query. A common vulnerable pattern looks like this:

SELECT * FROM users WHERE username = '{{user_input}}';

If user_input is intended to be a username like alice, the query becomes: SELECT * FROM users WHERE username = 'alice';

Harmless, right? But what if an attacker enters ' OR '1'='1 as the user_input? The query then becomes: SELECT * FROM users WHERE username = '' OR '1'='1';

Let’s break that down:

  • username = '': This part will likely return no results.
  • OR '1'='1': This condition is always true.
  • The entire WHERE clause becomes FALSE OR TRUE, which evaluates to TRUE.

Result? The query now effectively becomes SELECT * FROM users WHERE TRUE;, which fetches all users, bypassing authentication or revealing sensitive data!

Impact:

The consequences of a successful SQLi attack can be severe:

  • Data Theft: Reading, modifying, or deleting sensitive data (user credentials, financial info).
  • Authentication Bypass: Logging in as any user, including administrators.
  • Remote Code Execution: In some database configurations (e.g., xp_cmdshell in MS SQL Server), attackers can execute operating system commands.
  • Denial of Service: Deleting tables or making the database inaccessible.

Safe Reproduction (Conceptual):

To demonstrate, we’ll use a simple Node.js application with sqlite3 for a lightweight, file-based database.

Vulnerable Code Snippet (Conceptual - we’ll build this step-by-step later):

// Imagine this is part of your server-side code
app.get('/users-vulnerable/:id', (req, res) => {
    const userId = req.params.id;
    const query = `SELECT username, email FROM users WHERE id = '${userId}';`; // <-- VULNERABLE!
    db.all(query, (err, rows) => {
        // ... handle results
    });
});

An attacker could send a request like /users-vulnerable/1%20OR%201%3D1 (URL-encoded ' OR 1=1) to potentially retrieve all user data.

Prevention (2026 Best Practices):

The gold standard for preventing SQL Injection is Prepared Statements with Parameterized Queries.

  1. Prepared Statements / Parameterized Queries (Primary Defense):

    • What they are: This technique separates the SQL logic from the data. You define the SQL query structure first, with placeholders for user input. Then, you pass the user input as separate parameters. The database engine then combines them safely, ensuring that the input is always treated as data, never as executable code.
    • How they work: When you use a prepared statement, the database compiles the query template first. Then, when you provide the values for the placeholders, the database simply inserts them as values, not as part of the SQL command itself. It’s like filling out a pre-printed form (the template) instead of writing the whole letter yourself.
    // Example using sqlite3 (Node.js)
    const userId = req.params.id;
    // The '?' is a placeholder. The database knows to treat whatever comes next as data for this spot.
    const query = `SELECT username, email FROM users WHERE id = ?;`;
    db.get(query, [userId], (err, row) => { // Pass userId as a separate parameter
        // ... handle results
    });
    
  2. Input Validation (Secondary Defense):

    • While not a primary defense against SQLi (prepared statements are), validating user input is still crucial. Ensure data types, lengths, and formats match expectations. For example, if userId should be an integer, reject anything that isn’t.
  3. Least Privilege:

    • Grant database users only the absolute minimum permissions they need. If an application only needs to read data, don’t give it permissions to delete tables. This limits the damage if an injection does occur.
  4. Object-Relational Mappers (ORMs):

    • Frameworks like Sequelize (Node.js), Hibernate (Java), or SQLAlchemy (Python) often use prepared statements under the hood when you use their built-in query methods. However, be cautious: if you use raw SQL queries within an ORM, you still need to ensure they are parameterized.

2.3 NoSQL Injection

NoSQL databases (like MongoDB, CouchDB, Cassandra) are popular for their flexibility and scalability. However, they are not immune to injection attacks, often dubbed “NoSQL Injection.”

What it is:

NoSQL Injection occurs when user input is used to construct NoSQL queries or commands without proper sanitization, allowing attackers to manipulate the query logic or execute arbitrary code.

How it works:

The mechanics vary depending on the specific NoSQL database and how it handles queries.

  • MongoDB Example: MongoDB queries are often JSON-like objects. If an application directly incorporates user input into these query objects, an attacker can inject operators. Consider a login query for MongoDB:

    // Vulnerable Node.js/MongoDB code
    app.post('/login-nosql-vulnerable', (req, res) => {
        const { username, password } = req.body;
        // Directly using user input to build the query object
        db.collection('users').findOne({ username: username, password: password }, (err, user) => {
            // ... authentication logic
        });
    });
    

    An attacker might send username: "admin", password: {"$ne": null}. The query object becomes: { username: "admin", password: { "$ne": null } } This query asks: “Find a user with username ‘admin’ whose password is not equal to null.” If an ‘admin’ user exists, and their password field is not null (which it almost certainly won’t be), the attacker bypasses authentication without knowing the password!

  • JavaScript Injection: Some NoSQL databases, like older versions of MongoDB, allowed server-side JavaScript execution (e.g., using $where operator). If user input was passed to these, it could lead to arbitrary code execution. Modern MongoDB has largely deprecated or secured these features, but the principle remains for other systems.

Impact:

  • Authentication Bypass: Gaining unauthorized access to accounts.
  • Data Exposure/Modification: Reading or changing sensitive data.
  • Denial of Service: Causing database errors or crashes.

Safe Reproduction (Conceptual):

We won’t build a full NoSQL demo here for brevity, but the concept for a vulnerable Node.js/Express app with MongoDB would involve:

// Vulnerable NoSQL query construction
// If 'username' comes directly from user input without validation/sanitization
const userQuery = req.body.username; // e.g., req.body.username = { "$ne": null }
db.collection('users').findOne({ username: userQuery }, (err, user) => {
    // ...
});

Prevention (2026 Best Practices):

  1. Strict Input Validation & Sanitization (Primary Defense):

    • This is even more critical for NoSQL than SQL. Validate all user inputs against expected data types, formats, and allowed characters before using them in queries.
    • Whitelisting: Only allow specific, known-good values or patterns. If a field should only contain alphanumeric characters, strip out anything else.
  2. Avoid Dynamic Query Construction:

    • Instead of building query objects directly from user input, use the database driver’s specific methods for safe query construction. Many NoSQL drivers offer parameterized-like capabilities or query builder patterns that prevent operator injection.
    • For example, in MongoDB, avoid directly embedding user input as keys or operators in query objects.
  3. Strict Schema Validation:

    • Enforce schemas for your NoSQL collections. This ensures that data conforms to expected structures and types, making it harder for attackers to inject unexpected data types or operators.
  4. Least Privilege:

    • Similar to SQL, restrict database user permissions to the minimum necessary.
  5. Disable Server-Side JavaScript/Eval (if applicable):

    • If your NoSQL database supports server-side JavaScript execution (like MongoDB’s $where operator in older versions), disable it unless absolutely essential, and if used, ensure strict input sanitization.

2.4 Command Injection

Command Injection is a severe vulnerability where an attacker can execute arbitrary operating system commands on the server hosting the application.

What it is:

Command Injection occurs when an application passes unsanitized user-supplied data to a system shell (e.g., Bash, PowerShell) without proper escaping, allowing the attacker to run commands on the underlying operating system.

How it works:

Many server-side languages provide functions to execute shell commands (e.g., system() in PHP, exec() or spawn() in Node.js, subprocess.run() in Python). If user input is directly concatenated into these commands, an attacker can use shell metacharacters to inject their own commands.

Common shell metacharacters include:

  • ;: Command separator (run command A, then command B)
  • &: Run command A in background, then command B
  • &&: Run command B only if command A succeeds
  • ||: Run command B only if command A fails
  • |: Pipe output of command A to input of command B

Vulnerable Example: Imagine a web application that allows a user to “ping” an IP address to check network connectivity:

// Vulnerable Node.js code
const { exec } = require('child_process');

app.get('/ping-vulnerable', (req, res) => {
    const ipAddress = req.query.ip; // e.g., 192.168.1.1
    // Concatenating user input directly into a shell command string
    exec(`ping -c 1 ${ipAddress}`, (error, stdout, stderr) => { // <-- VULNERABLE!
        if (error) {
            console.error(`exec error: ${error}`);
            return res.status(500).send(`Error: ${stderr}`);
        }
        res.send(`<pre>${stdout}</pre>`);
    });
});

If an attacker provides ip=127.0.0.1; ls -la / The command executed on the server becomes: ping -c 1 127.0.0.1; ls -la /

This will first ping 127.0.0.1 and then, because of the ; metacharacter, execute ls -la /, listing the contents of the root directory of the server! Imagine the damage if they ran rm -rf / (though hopefully, permissions would prevent that).

Impact:

  • Remote Code Execution (RCE): The most severe impact, allowing full control over the server.
  • Data Exfiltration: Reading sensitive files (e.g., /etc/passwd, application configuration files).
  • Malware Deployment: Installing malicious software on the server.
  • Denial of Service: Shutting down services or deleting critical files.

Safe Reproduction (Conceptual):

We’ll build a simple Node.js application using child_process.exec to demonstrate this later. The attack involves sending a crafted ip parameter to the vulnerable endpoint.

Prevention (2026 Best Practices):

  1. Avoid Executing Shell Commands with User Input (Primary Defense):

    • The best defense is to simply avoid calling shell commands with any user-supplied input unless absolutely critical. Can you achieve the functionality using safer, built-in library functions?
  2. Use Safer Alternatives for Command Execution:

    • If you must execute external commands, use functions that explicitly separate the command from its arguments, preventing shell metacharacter interpretation.

    • Node.js: Use child_process.spawn() or child_process.execFile() instead of child_process.exec().

      • spawn() takes the command and its arguments as separate array elements:
      const { spawn } = require('child_process');
      
      app.get('/ping-safe', (req, res) => {
          const ipAddress = req.query.ip;
          // Arguments are passed as an array, preventing shell interpretation
          const ping = spawn('ping', ['-c', '1', ipAddress]); // <-- SAFE!
      
          let stdout = '';
          let stderr = '';
      
          ping.stdout.on('data', (data) => { stdout += data.toString(); });
          ping.stderr.on('data', (data) => { stderr += data.toString(); });
      
          ping.on('close', (code) => {
              if (code !== 0) {
                  console.error(`ping process exited with code ${code}`);
                  return res.status(500).send(`Error: ${stderr}`);
              }
              res.send(`<pre>${stdout}</pre>`);
          });
      });
      

      In this safe version, ipAddress is passed as a distinct argument to the ping command. Even if ipAddress contains ; ls -la /, it will be treated as part of the IP address string, not as a separate command.

  3. Strict Input Validation & Whitelisting:

    • If external commands are unavoidable, rigorously validate and whitelist user input. For an IP address, only allow valid IP address formats. Reject any input containing shell metacharacters.
  4. Least Privilege:

    • Run your application with the lowest possible privileges on the operating system. This limits what an attacker can do even if they manage to inject a command.

Step-by-Step Implementation: SQL Injection Demo

Let’s get hands-on and build a simple Node.js application to demonstrate SQL Injection and its prevention.

Goal: Create a user lookup feature, first with a vulnerability, then fix it.

Step 1: Initialize Project and Install Dependencies

First, create a new project directory and initialize a Node.js project.

  1. Create Project Folder:

    mkdir sql-injection-demo
    cd sql-injection-demo
    
  2. Initialize Node.js Project:

    npm init -y
    

    This creates a package.json file.

  3. Install Dependencies: We’ll need express for our web server and sqlite3 for a lightweight database.

    (As of 2026-01-04, these are recent stable versions. express v5 is in beta, so v4 is standard for production.)

Step 2: Set up the Server and Database

Create an app.js file in your sql-injection-demo directory.

  1. Basic Express Server Setup: Add the following code to app.js:

    // app.js
    const express = require('express');
    const sqlite3 = require('sqlite3').verbose();
    const app = express();
    const port = 3000;
    
    // Middleware to parse JSON bodies (not strictly needed for this demo, but good practice)
    app.use(express.json());
    
    // Connect to SQLite database
    // The database will be created as 'users.db' if it doesn't exist
    const db = new sqlite3.Database('./users.db', sqlite3.OPEN_READWRITE | sqlite3.OPEN_CREATE, (err) => {
        if (err) {
            console.error('Error connecting to database:', err.message);
        } else {
            console.log('Connected to the SQLite database.');
            // Create users table if it doesn't exist
            db.run(`CREATE TABLE IF NOT EXISTS users (
                id INTEGER PRIMARY KEY AUTOINCREMENT,
                username TEXT NOT NULL UNIQUE,
                password TEXT NOT NULL,
                email TEXT NOT NULL
            );`, (createErr) => {
                if (createErr) {
                    console.error('Error creating users table:', createErr.message);
                } else {
                    console.log('Users table ensured.');
                    // Insert some dummy data if the table is empty
                    db.get("SELECT COUNT(*) AS count FROM users", (countErr, row) => {
                        if (countErr) {
                            console.error('Error checking user count:', countErr.message);
                            return;
                        }
                        if (row.count === 0) {
                            db.run(`INSERT INTO users (username, password, email) VALUES
                                ('alice', 'password123', '[email protected]'),
                                ('bob', 'securepass', '[email protected]'),
                                ('charlie', 'mysecret', '[email protected]');`, (insertErr) => {
                                    if (insertErr) {
                                        console.error('Error inserting dummy data:', insertErr.message);
                                    } else {
                                        console.log('Dummy user data inserted.');
                                    }
                                });
                        }
                    });
                }
            });
        }
    });
    
    // Root route
    app.get('/', (req, res) => {
        res.send('Welcome to the SQL Injection Demo!');
    });
    
    // Start the server
    app.listen(port, () => {
        console.log(`Server running at http://localhost:${port}`);
    });
    

    Explanation:

    • We import express and sqlite3.
    • We create an sqlite3.Database instance, pointing to users.db. OPEN_READWRITE | sqlite3.OPEN_CREATE ensures it’s created if it doesn’t exist.
    • Upon successful connection, we run a CREATE TABLE IF NOT EXISTS command to set up our users table.
    • We then insert some dummy user data if the table is initially empty.
    • A basic root route and server listener are set up.
  2. Run the Server:

    node app.js
    

    You should see “Connected to the SQLite database.” and “Users table ensured.” (and “Dummy user data inserted.” the first time). Visit http://localhost:3000 in your browser.

Step 3: Implement a VULNERABLE User Lookup Endpoint

Now, let’s add an endpoint that is susceptible to SQL Injection. Add this code to your app.js before app.listen():

    // app.js (add this before app.listen())

    // VULNERABLE endpoint: Search for a user by username
    app.get('/users-vulnerable', (req, res) => {
        const username = req.query.username; // Get username from query parameter
        if (!username) {
            return res.status(400).send('Username parameter is required.');
        }

        // DANGER: Concatenating user input directly into the SQL query
        const query = `SELECT id, username, email FROM users WHERE username = '${username}';`;
        console.log('Vulnerable Query:', query); // Log the query for observation

        db.all(query, (err, rows) => {
            if (err) {
                console.error('Vulnerable Query Error:', err.message);
                return res.status(500).send('Database error.');
            }
            if (rows.length > 0) {
                res.json(rows);
            } else {
                res.status(404).send('User not found.');
            }
        });
    });

Explanation:

  • This endpoint (/users-vulnerable) expects a username query parameter.
  • The query string is constructed by directly embedding the username using string concatenation. This is the vulnerability point.
  • db.all() executes the query and returns all matching rows.

Step 4: Perform a SQL Injection Attack

Restart your server (Ctrl+C then node app.js).

  1. Normal Usage: Open your browser or use curl:

    • http://localhost:3000/users-vulnerable?username=alice
    • You should see: [{"id":1,"username":"alice","email":"[email protected]"}]
    • Check your server console; you’ll see Vulnerable Query: SELECT id, username, email FROM users WHERE username = 'alice';
  2. Injection Attack (Authentication Bypass): Now, try this malicious input:

    • http://localhost:3000/users-vulnerable?username=' OR '1'='1
    • Note: You might need to URL-encode the payload for some browsers/tools. A safer way to test this in a browser is: http://localhost:3000/users-vulnerable?username=%27%20OR%20%271%27%3D%271 (The %27 is ', %20 is space, %3D is =)

    You should now see all users:

    [
      {"id":1,"username":"alice","email":"[email protected]"},
      {"id":2,"username":"bob","email":"[email protected]"},
      {"id":3,"username":"charlie","email":"[email protected]"}
    ]
    

    On your server console, you’ll see the injected query: Vulnerable Query: SELECT id, username, email FROM users WHERE username = '' OR '1'='1';

    Congratulations! You’ve just successfully performed a SQL Injection attack (safely, on your own machine). This demonstrates how untrusted input, when directly concatenated, can alter the database’s intended query.

Step 5: Implement a SECURE User Lookup Endpoint

Now, let’s fix the vulnerability using prepared statements. Add this code to your app.js before app.listen():

    // app.js (add this before app.listen())

    // SECURE endpoint: Search for a user by username using prepared statements
    app.get('/users-secure', (req, res) => {
        const username = req.query.username;
        if (!username) {
            return res.status(400).send('Username parameter is required.');
        }

        // SAFE: Using a placeholder '?' and passing parameters separately
        const query = `SELECT id, username, email FROM users WHERE username = ?;`;
        console.log('Secure Query Template:', query); // Log the template

        db.all(query, [username], (err, rows) => { // Pass username in an array as a parameter
            if (err) {
                console.error('Secure Query Error:', err.message);
                return res.status(500).send('Database error.');
            }
            if (rows.length > 0) {
                res.json(rows);
            } else {
                res.status(404).send('User not found.');
            }
        });
    });

Explanation:

  • The query string now uses a ? as a placeholder for the username.
  • Crucially, db.all() is called with [username] as the second argument. This tells sqlite3 to treat username as a literal value to be inserted into the placeholder, not as part of the SQL command itself.

Step 6: Test the Secure Endpoint

Restart your server (Ctrl+C then node app.js).

  1. Normal Usage:

    • http://localhost:3000/users-secure?username=alice
    • You should still see: [{"id":1,"username":"alice","email":"[email protected]"}]
    • Server console: Secure Query Template: SELECT id, username, email FROM users WHERE username = ?; (Notice the ? remains, the value is handled internally).
  2. Attempt Injection Attack:

    • http://localhost:3000/users-secure?username=' OR '1'='1
    • Or URL-encoded: http://localhost:3000/users-secure?username=%27%20OR%20%271%27%3D%271

    This time, you should get: User not found. The database correctly interprets the entire string ' OR '1'='1 as a username that simply doesn’t exist, preventing the injection. Success!

Mini-Challenge: Command Injection Prevention

Now that you’ve seen SQL Injection in action, let’s apply the principles to Command Injection.

Challenge: You need to create a new endpoint in your app.js that checks if a file exists on the server.

  1. Implement a vulnerable endpoint /file-check-vulnerable that takes a filename query parameter and uses child_process.exec to run ls -l <filename>.
  2. Demonstrate a command injection attack on this endpoint to list the contents of the root directory (/).
  3. Implement a secure endpoint /file-check-secure that achieves the same functionality but uses child_process.spawn to prevent command injection.
  4. Verify that your attack payload fails on the secure endpoint.

Hint:

  • For child_process.exec, the vulnerable part is directly concatenating filename into the command string.
  • For child_process.spawn, remember it takes the command and its arguments as separate array elements. The command for ls -l <filename> would be ls and its arguments ['-l', filename].
  • Test with filename=app.js first, then try filename=app.js; ls -la / (or URL-encoded app.js%3B%20ls%20-la%20%2F).

What to observe/learn: Pay close attention to how the child_process functions handle the user input. The vulnerable one will process shell metacharacters, while the secure one will treat the entire input string as a single argument.

Common Pitfalls & Troubleshooting

Even with good intentions, developers can sometimes make mistakes that leave them vulnerable.

  1. Forgetting Prepared Statements for All User Inputs: It’s easy to remember prepared statements for login forms, but what about search fields, profile updates, or even seemingly innocent ID parameters? Every piece of user-supplied data used in an SQL query must be parameterized.
  2. Relying Solely on Client-Side Validation: Client-side validation (e.g., JavaScript in the browser) is great for user experience, but it’s easily bypassed by attackers. Always perform server-side validation as your primary defense.
  3. Misconfiguring ORMs: While ORMs generally use prepared statements, if you’re using raw SQL queries within your ORM or an ORM has a specific feature for dynamic query construction that you misuse, you can still introduce injection flaws. Always consult the ORM’s documentation for secure usage.
  4. Using eval() or Dynamic Query Construction in NoSQL: Dynamically building NoSQL query objects based on raw user input, especially if it allows attackers to introduce operators or specific data types that alter query logic, is a major pitfall. Avoid server-side eval() or similar functions unless absolutely necessary and with extreme sanitization.
  5. Not Understanding Shell Metacharacters: For command injection, it’s crucial to understand how different operating systems’ shells interpret special characters. What works on Bash (&, ;, |) might be different for PowerShell. When using exec, assume any special character can be used to chain commands.

Troubleshooting:

  • “User not found” on a valid input after fixing: Double-check your prepared statement syntax. Did you use the correct placeholder (? for SQLite/MySQL, $1 for PostgreSQL, etc.) and pass the parameters correctly (e.g., as an array)?
  • “Database error” or syntax error: Log the final query string (for vulnerable cases) or the query template and parameters (for secure cases) just before execution. This helps pinpoint where the query became malformed or where an injection was attempted.
  • Command Injection not working on secure endpoint: Ensure you’re using spawn or execFile and passing arguments as an array. If you’re still seeing unintended commands, your input validation might be too lenient, or you might be using a function that still invokes a shell implicitly (e.g., exec with a single argument string).

Summary

Phew! That was a deep dive into Injection Flaws, a truly critical area of web security. Let’s recap the key takeaways:

  • Injection is about confusing interpreters: Untrusted user input gets treated as executable code or commands by a database, operating system shell, or other backend component.
  • SQL Injection (SQLi) targets databases: Attackers manipulate SQL queries to steal, modify, or delete data, or bypass authentication.
  • NoSQL Injection targets NoSQL databases: Similar to SQLi, but exploits dynamic query construction or server-side script execution in NoSQL systems.
  • Command Injection targets the OS: Attackers execute arbitrary operating system commands on the server, potentially leading to full server compromise.
  • The Golden Rule for Prevention: Separate code from data.
    • For SQLi: Use Prepared Statements / Parameterized Queries. Never concatenate user input directly into SQL strings.
    • For NoSQL Injection: Employ Strict Input Validation & Sanitization, avoid dynamic query construction from raw input, and enforce schemas.
    • For Command Injection: Avoid executing shell commands with user input entirely. If unavoidable, use safer functions like child_process.spawn() (Node.js) that treat arguments as literal data, and apply rigorous input validation.
  • Layered Defenses: Input validation, least privilege, and secure configurations are crucial secondary defenses for all injection types.

By consistently applying these prevention techniques, you can significantly harden your web applications against one of the most common and dangerous attack vectors. You’re now equipped with the knowledge and practical skills to identify and prevent injection flaws.

Next up, we’ll tackle another top contender on the OWASP list: Broken Authentication. Get ready to learn how to keep user accounts secure!

References


This page is AI-assisted and reviewed. It references official documentation and recognized resources where relevant.