Boolean-Based Blind SQL Injection: Extracting Data One Bit at a Time
When an injectable endpoint displays no database output, it can still answer yes-or-no questions through response differences. Learn how the oracle works, when it is reliable, and how parameterization removes it.
42 AND 1=1At a glance
CWE-89- Interpreter
- SQL database engine
- Required condition
- An injectable condition causes a stable true-versus-false response difference.
- Potential impact
- Blind extraction of sensitive values despite no database output being displayed.
- Primary defense
- Parameterize every value and remove attacker control over query structure.
Safe practice: use benign proof only, in a disposable local lab or on a system you are explicitly authorized to test.
On this page
Most introductions to SQL injection show the data appearing right on the page through a UNION SELECT. Real targets are rarely that generous. Often the query result is never echoed back: the endpoint returns the same product, the same "OK", the same redirect no matter what you inject. That does not make it safe — it makes it blind.
Boolean-based blind SQL injection is the technique for these endpoints. Instead of asking the database to show you data, you ask it true/false questions and read the answer from how the page responds. This post covers how inference works, how to distinguish a real oracle from ordinary page variation, and why a harmless true/false pair is enough to prove the flaw.
When this technique applies
Boolean-based testing needs three conditions: an injectable expression, a predicate the database evaluates, and a repeatable difference between true and false. The difference may be a status code, redirect, row count, word, header or response length. If both branches produce the same observable result, this technique has no channel and a tester must stop or use a different authorized method.
Caching, personalization and rotating content can imitate a boolean signal. Compare normalized responses across repeated baseline, true and false requests before concluding that the condition reached SQL.
How it happens
The root cause is identical to classic SQLi — input concatenated into a query — but the output is gone. Consider an endpoint that loads an article by id and concatenates that id straight into SQL:
$id = $_GET['id']; $row = $db->query("SELECT title, body FROM articles WHERE id = $id AND published = 1")->fetch(); if ($row) { render_article($row); } else { http_response_code(404); }
The page never prints the query's internals. But it has two clearly distinguishable states: a 200 with the article, or a 404. That binary difference is the entire side channel an attacker needs.
A concrete attack: true/false inference
First, confirm the parameter is injectable by forcing each branch. A condition that is always true should return the article; an always-false one should not:
42 AND 1=1 42 AND 1=2
If those two requests differ consistently, the parameter may be a reliable oracle. For a safe lab proof, attach a question about a fixed constant rather than production data:
42 AND ASCII(SUBSTRING('A', 1, 1)) = 65
If the article renders, the database evaluated the expression and confirmed that A has byte value 65. Pair it with = 66; the false response should be stable. This proves that attacker-supplied SQL logic controls the branch without selecting any private row.
The endpoint never returns query data. It returns one decision bit. That is enough to prove the boundary failure, and in a real attack repeated decisions could infer values the database account can read.
Why one bit is still serious
An attacker can ask ordered questions and use binary search to reduce the number of requests needed for each character. The important defensive point is not the extraction algorithm; it is that a stable yes/no response can expose any value readable by the vulnerable database account. An authorized assessment should record the paired proof and stop rather than infer real credentials or personal data.
Per-engine nuances
The shape of string functions changes with the database, so detection must understand the deployed dialect:
SUBSTRING('ABC', 1, 1) SUBSTR('ABC', 1, 1) ASCII('A') UNICODE('A')
The true/false signal also varies: a numeric context may need no quote, a string context needs ' and a comment terminator, and some endpoints leak the boolean through response length, a header, or timing rather than the status code. Identifying the correct oracle for the target is most of the work.
Signals that are not proof
- One response differs once but not on repetition.
- Both
1=1and1=2produce the same changed page. - The difference disappears when cache-busting and session state are controlled.
- The parameter changes normal application filtering without entering SQL syntax.
- A WAF blocks one payload but the application never evaluates either condition.
The fix: parameterize and constrain
Boolean-based blind SQLi dies the same way every other injection does — the input must never be parsed as SQL. Bind the id as a value so 42 AND 1=1 becomes a (failed) lookup for the literal string 42 AND 1=1:
using var cmd = new SqlCommand( "SELECT title, body FROM articles WHERE id = @id AND published = 1", connection); cmd.Parameters.Add("@id", SqlDbType.Int).Value = id;
Cast the id to its real type at the boundary, so non-numeric input is rejected before it reaches the database. Then run the query under a least-privilege account that cannot read the users table at all, so even a missed sink cannot reach the secrets the oracle was built to extract.
How SelfSec finds it
SelfSec's SQL injection engine covers 19 database families and treats every parameter as a potential blind oracle: it pairs always-true and always-false conditions, measures the response difference across status, length, content or timing, and requires the contrast to repeat before reporting a finding. Each result includes a reproduction request you can replay. Core scan processing runs locally on 127.0.0.1.
References
SelfSec is intended strictly for authorized security testing of systems you own or are explicitly permitted to assess.
Do both things about SQL Injection
SelfSec covers this class from both sides — the scanner confirms it in your own app, the firewall blocks it in front of your origin while the fix ships.