SQL injection (SQLi) is a vulnerability class — tracked as CWE-89 — where an application inserts untrusted input directly into a SQL query, letting an attacker rewrite the query's logic instead of just supplying data. It has held a spot on the OWASP Top 10 since the list's first release in 2003, and it remains bundled into the Injection category (A03) in the 2021 edition. The mechanism is simple enough to demo in a college classroom: a login form that concatenates a username string into SELECT * FROM users WHERE username = '...' can be broken with the seven characters ' OR '1'='1. The consequences are not simple at all. Albert Gonzalez's crew used SQL injection to steal more than 130 million card numbers from Heartland Payment Systems in 2008, and the Cl0p ransomware group used a single SQL injection flaw, CVE-2023-34362, to hit more than 2,600 organizations through the 2023 MOVEit Transfer breach. This entry explains what SQLi is, how it works, and how to stop it.
What Is SQL Injection (SQLi)?
SQL injection is a security flaw that occurs when an application builds a SQL statement by directly inserting untrusted input — form fields, URL parameters, HTTP headers, API payloads — into the query string instead of treating that input strictly as data. Because SQL doesn't distinguish between "code" and "data" once they're concatenated into the same string, an attacker who controls part of that string can add their own clauses, operators, or subqueries and change what the database actually executes. MITRE's CWE database defines this precisely as CWE-89, "Improper Neutralization of Special Elements used in an SQL Command," and it's a subclass of the broader CWE-943 injection family that also covers NoSQL, LDAP, and command injection. CWE-89 has appeared in MITRE's CWE Top 25 Most Dangerous Software Weaknesses every year since the list started in 2019, ranking #6 in the 2023 edition with 5,932 CVEs analyzed in that assessment — a two-decade-old bug class that new code keeps reintroducing.
How Does a SQL Injection Attack Work?
A SQL injection attack works by supplying input that changes a query's boolean logic, structure, or execution path rather than just its data values. Take a query built as "SELECT * FROM accounts WHERE username = '" + user + "' AND password = '" + pass + "'". If the application submits the raw user value without escaping or parameterizing it, an attacker entering admin'-- as the username truncates everything after the double dash as a SQL comment, turning the query into SELECT * FROM accounts WHERE username = 'admin' — no password required. From there, attackers escalate using UNION-based injection to append a second SELECT that pulls data from unrelated tables (' UNION SELECT credit_card, cvv FROM payments--), error-based injection to leak database structure through deliberately malformed queries that trigger verbose error messages, and blind injection when no output is visible at all — inferring answers one bit at a time through boolean responses (' AND 1=1-- vs. ' AND 1=2--) or through timing, such as '; IF (1=1) WAITFOR DELAY '0:0:5'-- on Microsoft SQL Server.
What Are the Different Types of SQL Injection?
There are three operational categories: in-band, blind (inferential), and out-of-band. In-band injection is the loudest and easiest to find — the attacker sends a payload and reads the result directly in the application's response, whether through UNION-based row injection or error-based messages that echo database internals like table names or DBMS version strings. Blind injection produces no visible output, so attackers rely on boolean-based queries that flip a page's behavior true/false, or time-based queries that use functions like SLEEP() (MySQL) or pg_sleep() (PostgreSQL) to measure response delay and confirm a condition indirectly. Out-of-band injection is the rarest and hardest to detect with a web proxy alone: it exfiltrates data through a completely separate channel, such as forcing the database server to make a DNS lookup or an HTTP request containing stolen data to an attacker-controlled domain, which is useful when firewalls block direct response-based exfiltration but still allow outbound DNS or HTTP traffic from the database tier.
How Severe Can a SQL Injection Vulnerability Be?
SQL injection routinely scores in the critical range because it can lead directly to full data exfiltration, authentication bypass, or remote code execution depending on database configuration. CVE-2023-34362, the Progress Software MOVEit Transfer flaw exploited in the 2023 Cl0p campaign, carries a CVSS v3.1 base score of 9.8; it let unauthenticated attackers escalate SQL injection into arbitrary file access and remote command execution on the underlying server, ultimately exposing data belonging to entities including the U.S. Department of Energy, British Airways, and Shell. Severity climbs further when the database account running the application has excessive privileges: MySQL's INTO OUTFILE or Microsoft SQL Server's xp_cmdshell stored procedure can turn a plain data-extraction bug into full server compromise if the connecting account has file-write or shell-execution rights it never needed. That's why CVSS scoring for SQLi findings frequently lands at High (7.0–8.9) or Critical (9.0–10.0) rather than Medium — the theoretical ceiling on damage is almost always "everything the database account can touch."
How Is SQL Injection Different from Command Injection or NoSQL Injection?
The difference is the target interpreter, not the underlying flaw pattern. SQL injection manipulates statements sent to a relational database engine (MySQL, PostgreSQL, Microsoft SQL Server, Oracle) using SQL syntax; command injection (CWE-78) manipulates strings passed to an operating system shell, letting an attacker chain commands with characters like ;, |, or backticks to run arbitrary OS-level commands instead of database queries. NoSQL injection targets document or key-value stores like MongoDB and typically abuses the query language's own operators — for example, submitting {"$gt": ""} as a login password field bypasses authentication in a MongoDB query the same way ' OR '1'='1 does in SQL, without ever touching a line of SQL syntax. All three share the same root cause under CWE-943 — untrusted input crossing into an interpreter without neutralization — which is why the fix pattern (separate code from data, validate structurally, never build queries or commands via string concatenation) generalizes across all of them even though the payload syntax differs completely.
How Do You Prevent SQL Injection?
The most effective prevention is parameterized queries (prepared statements), which send the query structure and the user-supplied values to the database separately so the engine never interprets input as executable SQL. In Java that means PreparedStatement with ? placeholders instead of string-concatenated Statement objects; in Python, cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) instead of an f-string; in PHP, PDO with bound parameters instead of raw mysqli_query() calls. OWASP's Injection Prevention Cheat Sheet identifies parameterization as closing the overwhelming majority of real-world SQL injection cases because it eliminates the ambiguity between code and data at the driver level, rather than trying to filter dangerous input after the fact. Supplementary controls matter for the cases parameterization can't reach directly, like dynamic column or table names in sorting logic — use strict allow-lists there, never blocklists, since blocklist filters on keywords like UNION or -- are routinely bypassed with case variation, inline comments, or encoding tricks. Least-privilege database accounts and web application firewalls add defense in depth, turning a successful injection attempt into a contained read rather than a full compromise.
How Safeguard Helps
Finding a SQL injection pattern in a static scan is easy; knowing whether it's reachable from an internet-facing entry point is what actually matters. Safeguard's reachability analysis traces the call path from untrusted input sources through your application into every function that touches a database query, so a flagged CWE-89 pattern in a rarely-used internal script doesn't consume the same triage priority as one sitting directly behind a public login form. Griffin AI, Safeguard's agentic security engine, correlates that reachability data with exploit intelligence and CVSS context to rank injection findings by real-world exploitability rather than raw scanner output, cutting through the noise that makes most SQLi backlogs unmanageable. Continuous SBOM generation and ingest keeps that analysis current as dependencies and internal services change, and when a fix is available, Safeguard opens auto-fix pull requests that swap concatenated query strings for parameterized statements — closing the gap between detection and remediation without waiting on a manual review cycle.