00:00

#0305

Second-Order Injection

Hard+200 XPA03:2021 InjectionCWE-89
SQL InjectionSecond-Order

Scenario

A user profile endpoint lets users update their bio. The handler stores the bio in the database — but builds the UPDATE query by concatenating both the bio and the userId inline.

Even if a frontend layer escapes the bio before display, the storage query itself is injectable. An attacker can craft a bio that contains SQL that fires when the bio is first written: '; UPDATE users SET role='admin' WHERE '1'='1.

This is called second-order (or stored) SQL injection: the payload is injected at write time, and may execute silently in the background or when the bio is later reused in another query.

Second-order injection is harder to detect than first-order because the injected payload is stored safely-looking text. It bypasses input validation at the API layer and can lie dormant until triggered by a different code path.

Your Tasks

  1. Fix buildProfileUpdateQuery so both bio and userId are passed as parameters.
  2. Return { sql: "UPDATE profiles SET bio=$1 WHERE user_id=$2", params: [bio, userId] }.
  3. The raw bio (including any quotes or SQL keywords) must end up in params, never in the SQL string.

Examples

Example 1Stored injection blocked

buildProfileUpdateQuery('u1', "'; UPDATE users SET role='admin' WHERE '1'='1")
// FIX → { sql: 'UPDATE profiles SET bio=$1 WHERE user_id=$2', params: ["'; UPDATE...", 'u1'] }

Example 2Normal bio update

buildProfileUpdateQuery('u1', 'I love coding')
// → { sql: '...SET bio=$1 WHERE user_id=$2', params: ['I love coding', 'u1'] }

Constraints

  • Only edit the function body — do not change the function signature.
  • The returned sql must use $1 for bio and $2 for userId.
  • The returned params array must be [bio, userId] in that order.
  • No external packages.

Hint

References

solution.js
Ln 1, Col 1UTF-8JavaScript
Sandbox ready
0/0/0not run