SQL injection (CWE-89) remains one of the most reliably exploitable vulnerabilities in web applications, cited in OWASP API Security Top 10 2023 API10. String-concatenated queries hand attackers a direct channel into the database: they can extract the full schema, dump all rows, bypass authentication, or in some configurations execute OS commands via xp_cmdshell. NoSQL injection (CWE-943) achieves the same ends with operator injection like { $where: ... } or { $gt: '' }. ORMs and parameterized queries separate code from data at the driver level — a string from user input can never become a SQL keyword or a query operator.
High because SQL and NoSQL injection give attackers direct read/write access to the entire database, potentially bypassing all application-level authorization.
Use ORM methods or parameterized placeholders for every database interaction. Never interpolate user-controlled strings into query text, including in ORDER BY clauses, table names, or JSON path expressions.
// Bad — never do this
const user = await pool.query(`SELECT * FROM users WHERE email = '${req.body.email}'`)
// Good — parameterized
const user = await pool.query('SELECT * FROM users WHERE email = $1', [req.body.email])
// Good — ORM (Prisma, Drizzle, Kysely all parameterize under the hood)
const user = await db.user.findUnique({ where: { email: req.body.email } })
// If dynamic column names are unavoidable, use an allowlist:
const SORTABLE = ['name', 'createdAt', 'email'] as const
const col = SORTABLE.includes(req.query.sort) ? req.query.sort : 'createdAt'
ID: api-security.input-validation.parameterized-queries
Severity: high
What to look for: Enumerate every relevant item. Examine all database query code (SQL queries, NoSQL operations, ORM calls). Look for string concatenation with user input, template literals that embed variables, or direct object merging for NoSQL. Verify that parameterized queries or ORMs are used instead.
Pass criteria: At least 1 of the following conditions is met. All database queries use parameterized placeholders or ORM methods. No user input is concatenated into query strings.
Fail criteria: Any database query concatenates user input directly, such as query('SELECT * FROM users WHERE id = ' + userId) or NoSQL queries merge user input directly into query objects.
Skip (N/A) when: The application does not use a database.
Detail on fail: Show the vulnerable pattern. Example: "SQL queries concatenate user ID directly: SELECT * FROM users WHERE id = ' + req.query.id" or "MongoDB queries merge user input directly: db.collection.findOne({ email: req.body.email })"
Remediation: Use parameterized queries or ORMs:
// Good: Parameterized query
const user = await db.query('SELECT * FROM users WHERE id = $1', [userId])
// Good: ORM
const user = await db.user.findUnique({ where: { id: userId } })
// Bad: String concatenation
const user = await db.query('SELECT * FROM users WHERE id = ' + userId)