SQL Injection
Improper Neutralization of Special Elements used in an SQL Command (SQL Injection)
What it is
CWE-89 occurs when an application constructs SQL queries by concatenating user input rather than using parameterized queries or prepared statements. An attacker who controls part of the query can modify its meaning: read arbitrary data, modify data, run administrative commands, or in the worst case execute arbitrary code via DBMS-specific features.
Why it matters
SQL injection has been in the CWE Top 25 every year it has been published. Despite three decades of disclosure, parameterized-query adoption is still incomplete, especially in legacy code, dynamically-built filter expressions, and ORM escape hatches. A single SQL injection in a customer database is sufficient for mass credential exfiltration, ransomware staging, or breach disclosure under HIPAA, PCI-DSS, GDPR, and SOX.
Common patterns
- •String concatenation: 'SELECT * FROM users WHERE id = ' + req.params.id
- •f-string or template-string queries in Python or Node that interpolate user input.
- •Dynamic WHERE-clause construction for filter UIs without proper parameter binding.
- •ORM .raw() / .query() / .execute() calls that bypass the ORM's own parameterization.
- •Stored procedures that use EXEC or sp_executesql on dynamically-built strings.
Languages affected
What Deva detects
Deva tracks data flow from request inputs through query builder calls to database driver execution. The scanner has language-specific rules for Sequelize, Knex, TypeORM, Prisma raw queries, sqlx, pg, mysql2, psycopg2, SQLAlchemy text(), Django raw(), Active Record connection.execute(), and JDBC PreparedStatement misuse. Findings include the specific driver call site, the upstream input source, and a suggested parameterization rewrite.
Example
Vulnerable
def get_user(user_id):
query = f"SELECT * FROM users WHERE id = {user_id}"
return db.execute(query).fetchone()Fixed
def get_user(user_id):
query = "SELECT * FROM users WHERE id = %s"
return db.execute(query, (user_id,)).fetchone()Explanation
The vulnerable version uses an f-string that interpolates user_id directly into the SQL. An attacker passing 1 OR 1=1 -- exfiltrates the entire users table. The fix uses parameterized execution with the database driver's placeholder syntax (varies by driver: %s for psycopg2, ? for sqlite3, $1 for asyncpg). The driver handles escaping correctly and the query plan is cached by the database.
Where this fits in OWASP Top 10
Compliance framework mapping
| Framework | Controls |
|---|---|
| OWASP Top 10 (2021) | A03:2021 Injection |
| NIST 800-53 Rev 5 | SI-10 Information Input Validation |
| PCI-DSS v4.0 | 6.2.4 Software engineering techniques |
| HIPAA Security Rule | 164.312(a)(1) Access Control164.312(c)(1) Integrity |
| CMMC 2.0 L2 | SI.L2-3.14.1 Flaw remediation |
Related CWEs
Deva detects CWE-89 alongside 970+ other CWE patterns at write time, with AI-assisted fix generation that maintains compliance.