SQL injection happens when an app builds a database query with untrusted input that isn't separated from the query's code, letting an attacker inject SQL and change what the query does. It arises from concatenating user input into the SQL string. Impact: read/modify/delete data (entire user tables, credentials, PII), authentication bypass, sometimes file access or command execution - usually a full data breach, hence critical. Types: in-band (results returned), blind (inferred via boolean/time), out-of-band. Blind variants are where manual testing shines. The definitive fix: parameterised queries / prepared statements - send SQL structure and values separately so values can't become code. Filtering alone is unreliable. Detail below; part of every web app test.
// 01 How SQL injection works
Applications talk to their database in SQL - a language of queries like “select the user whose email is X.” The vulnerability appears when the app builds that query by concatenating user input directly into the SQL string. Imagine a login that constructs SELECT * FROM users WHERE email = '<input>' AND password = '<input>'. A normal user supplies an email; an attacker supplies SQL syntax - a value like ' OR '1'='1 - and the query becomes one that's always true, returning a user without the right password. The database can't tell the attacker's data from the developer's code, because they were mixed into one string. That's the whole vulnerability: user input crossing from data into the query's code. It's the same root cause as command injection, XSS and the other injection classes - a trust boundary that wasn't enforced.
// 02 What an attacker gains
SQL injection is critical because the database usually holds the crown jewels. With a working injection an attacker can: read sensitive data - entire tables of user records, credentials, and personal or financial information; modify or delete data; and bypass authentication by manipulating the login query to return a valid user without the password. Depending on the database and its configuration, they may also read or write files on the server, escalate privileges within the database, and in some cases reach command execution on the host. Because a single injection point often exposes the whole dataset, an exploitable SQLi is frequently a full data breach - the exact outcome the PDPL and breach-cost figures are about. That severity is why it remains a top-tier finding despite being decades old.
// 03 The types of SQL injection
In-band
The classic: results (or errors) come back in the response, so the attacker reads data directly - error-based or UNION-based.
Blind (boolean)
No data returned, but true/false conditions cause small response differences - extract data one answer at a time.
Blind (time)
Inject a conditional delay - a true condition makes the response slow - revealing answers through timing.
Out-of-band
Make the database contact an attacker-controlled server (e.g. DNS), proving injection and exfiltrating data.
The blind variants matter most in practice: modern apps often suppress errors and don't echo query results, so a real injection returns nothing obvious. A scanner may miss it; a human confirms it with boolean or time-based inference and extracts the data anyway.
// 04 How to test for it
Testing probes every input that could reach a query - not just obvious form fields but URL parameters, headers, cookies and JSON body values. The tester submits SQL metacharacters (a lone quote, comment sequences) and observes for errors or behaviour changes; injects boolean conditions (' AND 1=1 vs ' AND 1=2) to see differential responses; and uses time-based payloads (a conditional sleep) to confirm blind injection where nothing else shows. Confirming impact means carefully demonstrating data retrieval or auth bypass within the rules of engagement, since it touches live data. Automated tools help find obvious cases, but the blind, second-order and context-specific injections - and safely proving impact without damage - are the manual craft that a real web app test brings, per our methodology.
// 05 How to prevent it for good
Unlike many bugs, SQL injection has a definitive, complete fix: parameterised queries (prepared statements). Instead of building a SQL string with user input in it, the app sends the query structure and the user-supplied values to the database separately - the database treats the values strictly as data, so they can never be interpreted as SQL code. This removes the vulnerability at the root, and it's the primary recommended defence everywhere. A modern ORM that parameterises by default gives the same protection - provided any raw query it allows is also parameterised (the common slip). Add defence-in-depth: least-privilege database accounts (so an injection can't reach beyond what the app needs), input validation against expected formats, and avoiding dynamic query construction. What doesn't reliably work is filtering dangerous characters - attackers have too many encodings and techniques. Parameterise, and the whole class disappears. It's checked in every web app engagement and code review.
// 06 Frequently asked questions
What is SQL injection?
A vulnerability where an app builds a database query with untrusted input that isn't separated from the query's code, letting an attacker inject SQL and change what the query does. It arises when developers concatenate user input into the SQL string. Because the query runs with the app's database privileges, it can let an attacker read, modify or delete data, bypass authentication, and sometimes execute commands.
What can an attacker do with it?
Read sensitive data (whole tables of user records, credentials, PII/financial data), modify or delete data, and bypass authentication by manipulating a login query. Depending on the database, also read/write files, escalate privileges, or reach command execution. Because the database holds the most sensitive data, an exploitable SQLi frequently means a full data breach - hence critical.
What is blind SQL injection?
When the app is vulnerable but doesn't return query results or errors, so the attacker can't see data directly. Testers extract it by inference: boolean-based asks true/false questions and observes small response differences; time-based injects a conditional delay so a true condition makes the response slow. Slower but just as effective - a hallmark of skilled manual testing over automated scanning.
How do you prevent SQL injection?
Use parameterised queries (prepared statements), which send the SQL structure and user values separately so values can never be interpreted as code - the definitive fix. An ORM that parameterises by default works too, as long as any raw query is also parameterised. Add least-privilege database accounts and input validation. Filtering dangerous characters alone is unreliable, so parameterisation is the control that closes it.
// 07 Related reading
- Command injection, XSS and SSTI — the injection family.
- OWASP Top 10 in real engagements and the flaws we find most.
- Web application penetration testing and source code review.