CWE-89CriticalMITRE entry

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

PythonJavaScriptTypeScriptJavaGoRubyPHPC#

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

FrameworkControls
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.