Web Application Security Checklist

Chapter 3 of 8 · 4 checks · 2 min

Injection (A05)

Injection happens when user input is treated as part of a query or command. These checks keep data and code apart.

Show
  1. Use parameterized queries for every SQL statementMust

    Why it matters and how to check

    String concatenation in SQL is still the classic injection bug. Use your driver's placeholders or an ORM query builder so values are sent separately from the query text. Where you must build dynamic parts, such as a sort column, map user input to a fixed allowlist of column names.

    To verify, search the codebase for template strings or + near SELECT, INSERT, UPDATE and ORDER BY, and for raw query helpers. Then send a single quote and ' OR '1'='1 in every search box and filter. You should see normal "no results" behaviour, never a database error or extra rows.

    Example

    TypeScript
    // Values travel separately from the SQL text
    const { rows } = await pool.query(
      'SELECT id, title FROM posts WHERE author_id = $1 AND status = $2',
      [userId, status]
    );
  2. Reject objects where you expect strings in NoSQL queriesMust

    Why it matters and how to check

    With MongoDB and similar stores, the risk is operator injection: a JSON body like {"email": {"$ne": null}} turns an equality check into "match anything". This bypasses login forms and filters when request fields go straight into a query.

    Validate the type of every field before it reaches the database, and cast ids and strings explicitly. To verify, send a login request whose email or password is an object containing $ne or $gt. It must fail validation with a 400, not sign you in or return a list.

  3. Never pass user input to a shellMust

    Why it matters and how to check

    Functions that run a command through a shell, like exec in Node or os.system in Python, will run anything after a ; or &&. If you need to call a program, use the form that takes the program and an argument array with no shell, and validate the arguments against an allowlist.

    To verify, search for exec(, execSync, shell=True, system( and backtick command strings in server code. For each hit, confirm no request data reaches it, or that it uses execFile or spawn with an argument array.

  4. Validate every request body against a schema on the serverShould

    Why it matters and how to check

    Schema validation is not a replacement for parameterized queries or output encoding, but it removes whole classes of surprises: wrong types, unexpected fields, huge strings and mass assignment of fields like role. Define a schema per endpoint, reject unknown keys, and set maximum lengths.

    To verify, pick five write endpoints and send a body with an extra field, a number where a string belongs and a 1 MB string. Each should return a 400 with a short message, and none of the extra data should appear in the database afterwards.