SQL Injection (CWE-89) is a vulnerability that lets an attacker insert malicious SQL into a database query by submitting unsanitized input. It allows unauthorized reading, modification, or deletion of data, and can bypass login authentication entirely. It falls under OWASP's A03:2021 — Injection category and remains one of the most exploited web vulnerabilities in production applications today.
| Field | Value |
|---|---|
| CWE ID | CWE-89 |
| OWASP Category | A03:2021 — Injection |
| CAPEC | CAPEC-66 — SQL Injection |
| Typical Severity | Critical to High (CVSS v3.1: 7.5–9.8) |
| Last Updated | 2026-07-21 |
Quick Summary
SQL Injection happens when an application builds a database query using untrusted input — a form field, URL parameter, header, or cookie — without separating that input from the query’s logic. The database can’t distinguish the intended query from the attacker’s injected code, so it executes both.
It matters because it is one of the oldest, most damaging, and still most common vulnerability classes on the web. A single unprotected input field can expose an entire database — customer records, credentials, and payment data — or let an attacker log in as any user without a password.
Jump to: Overview · Technical Explanation · Business Impact · Attack Scenario · Detection · Remediation · Framework-Specific Fixes · Best Practices Checklist · FAQ · Related Vulnerabilities · References
Overview
What: SQL Injection is a flaw where an application interprets user-supplied input as executable SQL code instead of plain data, because the input was concatenated directly into a query string.
Why it matters: A successful injection can read, modify, or delete any data the application’s database account can access — and in some configurations, escalate to running commands on the underlying server.
Where it occurs: Anywhere user input reaches a database call without parameterization — login forms, search boxes, filters, sort parameters, API query parameters, even HTTP headers or cookies if they’re used in a query.
Who is affected: Any application backed by a relational database (MySQL, PostgreSQL, SQL Server, Oracle, SQLite) that builds queries by string concatenation. This includes customer-facing apps, internal admin panels, and legacy tools that often skip the same security review as public-facing code.
Technical Explanation
Root Cause
The root cause is always the same: user input and SQL code share the same string, and the database has no way to tell them apart. This happens when developers build queries with string concatenation, f-strings, or template literals instead of parameterized queries.
Attack Flow
- Application accepts input from the user (e.g. a username field)
- Input is concatenated directly into a SQL query string
- Query is sent to the database as-is
- Database executes the attacker’s injected SQL as part of the query
- Attacker receives unauthorized data, or bypasses a check (like a login)
Vulnerable Code
query = "SELECT * FROM users WHERE username = '" + username + "' AND password = '" + password + "'"
If username is submitted as ' OR '1'='1' -- , the executed query becomes:
SELECT * FROM users WHERE username = '' OR '1'='1' -- ' AND password = ''
OR '1'='1' makes the condition always true, and -- comments out the rest of the query — the attacker logs in without valid credentials.
Secure Code
cursor.execute(
"SELECT * FROM users WHERE username = %s AND password = %s",
(username, password)
)
Parameterized queries send the input separately from the query structure, so the database always treats it as data, never as code — this is the complete fix, not just a mitigation.
Business Impact
- Data theft — full read access to the database, including customer PII, credentials, and payment data
- Financial loss — breach response, regulatory fines, and customer churn following a disclosed incident
- Compliance exposure — a confirmed SQL injection is a reportable incident under SOC 2, GDPR, and PCI-DSS
- Reputation damage — public breach disclosures are one of the most damaging events for customer trust in a SaaS product
- Full compromise — on some database configurations, SQL injection can be escalated to run OS commands on the underlying server
Attack Scenario
- An attacker finds a login form that passes username and password into a query without parameterization
- They submit
admin' --as the username, leaving the password field blank - The trailing
--comments out the password check entirely - The application logs the attacker in as
admin— no valid password required - The same flaw in a search box or filter parameter can instead be used with
UNION SELECTstatements to pull arbitrary columns from other tables, one query at a time — extracting the full database without ever logging in
Detection
Manual Testing
Submit common SQLi payloads (', ' OR '1'='1, '; DROP TABLE) into every input and observe query errors, timing delays, or content differences in the response.
Automated Scanners (SAST / DAST)
- DAST — payload-based fuzzing against a running application (e.g. OWASP ZAP, Wapiti) that sends SQLi payloads to every input and checks for behavioral differences
- SAST — static code review that flags string-concatenated queries anywhere user input reaches a database call, before the code ever ships
PenScan Detection
PenScan’s ZAP and Wapiti scanner engines actively test every discovered input with SQLi payloads and report the exact injection point, the payload used, and the affected parameter — not just a generic “vulnerable” flag.
Remediation
- Use parameterized queries / prepared statements for every database call that includes user input — the primary, complete fix
- Use an ORM (SQLAlchemy, Sequelize, ActiveRecord, Hibernate) — these parameterize queries by default when used through their standard query builder
- Apply least privilege to the database account the application uses, so even a successful injection has limited reach
- Allow-list input where parameterization isn’t possible (e.g. sort-column names) as defense-in-depth, not a replacement for parameterization
- Never build queries by string concatenation or f-strings/template literals with user input, even for fields that seem “internal” or “trusted”
Framework-Specific Fixes
Java (JDBC)
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM orders WHERE id = ?");
stmt.setInt(1, orderId);
Spring Boot (Spring Data JPA)
@Query("SELECT o FROM Order o WHERE o.id = :orderId")
Order findByOrderId(@Param("orderId") Long orderId);
ASP.NET (C#, Entity Framework / ADO.NET)
var cmd = new SqlCommand("SELECT * FROM Orders WHERE Id = @id", connection);
cmd.Parameters.AddWithValue("@id", orderId);
Node.js (mysql2)
connection.query('SELECT * FROM orders WHERE id = ?', [orderId]);
Django (ORM)
Order.objects.filter(id=order_id) # parameterized automatically
# If raw SQL is unavoidable:
Order.objects.raw('SELECT * FROM orders WHERE id = %s', [order_id])
Laravel (Eloquent / Query Builder)
DB::table('orders')->where('id', $orderId)->get();
PHP (PDO)
$stmt = $pdo->prepare('SELECT * FROM orders WHERE id = :id');
$stmt->execute(['id' => $orderId]);
Best Practices Checklist
- ✅ Parameterized queries / prepared statements for every user-influenced query
- ✅ ORM used through its standard query builder, not raw SQL
- ✅ Input validation and allow-listing as defense-in-depth
- ✅ Least-privilege database accounts for application connections
- ✅ No dynamic SQL built via string concatenation, f-strings, or template literals
- ✅ Detailed database errors never returned to the client
- ✅ Regular automated scanning (DAST) alongside code review (SAST)
- ✅ Web Application Firewall as a mitigating control, not a substitute for fixing the code
FAQ
What is SQL Injection?
SQL Injection (CWE-89) is a vulnerability where an attacker manipulates a database query by inserting malicious SQL through unsanitized user input, letting them read, modify, or delete data they shouldn’t have access to.
How does SQL Injection work?
It works because the application concatenates user input directly into a SQL query string. The database can’t tell the difference between the intended query and the attacker’s injected code, so it executes both as one statement.
Can SQL Injection steal passwords?
Yes. If password hashes are stored in a database reachable through an injectable query, an attacker can extract them directly using UNION SELECT statements, then attempt to crack them offline.
How do you prevent SQL Injection?
Use parameterized queries (prepared statements) for every database call that includes user input. This is the complete fix — input validation and WAFs are useful additional layers, but they don’t replace parameterization.
Does using an ORM fully protect against SQL Injection?
Mostly, but not automatically. ORMs are safe when used through their standard query builder methods. They become vulnerable again if a developer drops into raw SQL and string-interpolates a value into it.
Can a Web Application Firewall (WAF) replace fixing the code?
No. A WAF blocks known attack patterns, but new payloads and encoding tricks regularly bypass WAF rules. It’s a mitigating control, not a fix — the query itself must be parameterized.
Is SQL Injection still relevant in 2026?
Yes. It remains in the OWASP Top 10 (A03: Injection) and is routinely found in production applications, especially legacy code, admin panels, and internal tools that skip the same security review as customer-facing apps.
How do I know if my site is affected?
Run an automated scan that actively tests every input with SQLi payloads, rather than only checking for known CVEs — this catches custom, first-party code that no public vulnerability database would know about.
Related Vulnerabilities
| CWE | Name |
|---|---|
| CWE-79 | Cross-Site Scripting (XSS) |
| CWE-78 | OS Command Injection |
| CWE-352 | Cross-Site Request Forgery (CSRF) |
| CWE-918 | Server-Side Request Forgery (SSRF) |
| CWE-943 | Improper Neutralization of Special Elements in Data Query Logic |
| CWE-564 | SQL Injection: Hibernate |
References
- MITRE CWE-89: SQL Injection
- OWASP A03:2021 – Injection
- MITRE CAPEC-66: SQL Injection
- NVD – National Vulnerability Database
Scan Your Own Site
Manual code review catches what you know to look for. An automated scan catches what you didn’t. Scan your own website using PenScan to find SQL injection and other OWASP Top 10 risks before an attacker does.