SQLMap Testing Guide: SQL Injection Detection, Tamper Scripts & CI Integration
SQLMap is the standard tool for automated SQL injection detection and exploitation. It supports every major injection technique, handles authentication, works with WAFs via tamper scripts, and integrates into CI pipelines. This guide covers practical testing workflows for development and security teams.
What SQLMap Tests
SQLMap detects and exploits SQL injection across six injection techniques:
| Technique | Description | When It's Used |
|---|---|---|
| Boolean-based blind | AND 1=1 vs AND 1=2 — infers data from true/false responses |
No error output, data in response |
| Time-based blind | AND SLEEP(5) — infers data from response delays |
No error output at all |
| Error-based | Forces database errors that leak data | Error messages visible |
| UNION-based | Appends UNION SELECT to extract data directly | Easiest to exploit |
| Stacked queries | Executes multiple statements with ; |
Write operations possible |
| Out-of-band | Uses DNS/HTTP callbacks | Firewall restrictions on other methods |
Basic Usage
Test a Single URL
# Test GET parameter
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/users?id=1"
<span class="hljs-comment"># Test specific parameter
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/users?id=1&format=json" -p <span class="hljs-built_in">id
<span class="hljs-comment"># Test POST data
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/search" \
--data=<span class="hljs-string">"query=test&category=all" \
-p queryTest with Headers
# With authentication cookie
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/account" \
--cookie=<span class="hljs-string">"session=abc123; csrf=xyz789"
<span class="hljs-comment"># With Bearer token
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/users" \
-H <span class="hljs-string">"Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."
<span class="hljs-comment"># Test a header for injection
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/data" \
-H <span class="hljs-string">"X-Forwarded-For: *" \
-p <span class="hljs-string">"X-Forwarded-For"Test from Burp Request File
Capture a request in Burp Suite or your proxy, save it to a file:
POST /api/search HTTP/1.1
Host: staging.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGci...
Cookie: session=abc123
{"query":"test","filters":{"category":"all","sort":"name"}}Run SQLMap against it:
sqlmap -r request.txt \
--data='{"query":"*","filters":{"category":"all"}}' \
--level=3 \
--risk=2The * marks the injection point in the request body.
Blind SQL Injection Detection
Blind injection is the hardest to detect manually — the application doesn't show database errors, and the injected data isn't reflected in responses. SQLMap automates the inference process.
Boolean-Based Blind
SQLMap sends pairs of requests where only the injected condition differs:
Request 1: id=1 AND 1=1 → 200 OK (normal response)
Request 2: id=1 AND 1=2 → 200 OK (empty response or different content)If responses differ, the parameter is injectable. SQLMap then binary-searches through the data character by character.
To test specifically for blind injection:
sqlmap -u "https://staging.example.com/api/product?id=1" \
--technique=B \
--level=5 \
--string=<span class="hljs-string">"Product Name" <span class="hljs-comment"># String present in legitimate response--string helps SQLMap distinguish true/false responses when status codes are the same.
Time-Based Blind
When responses look identical regardless of payload:
sqlmap -u "https://staging.example.com/api/search" \
--data=<span class="hljs-string">"q=test" \
--technique=T \
--time-sec=3 \
--level=3SQLMap sends payloads like q=test' AND SLEEP(3)-- and measures response time. Reliable but slow — 3 seconds per inference step means extracting a 20-character password takes minutes.
Detecting Blind Injection Without SQLMap
Manual verification before running SQLMap:
# Check for time delay
<span class="hljs-keyword">time curl <span class="hljs-string">"https://staging.example.com/api/product?id=1%27%20AND%20SLEEP(5)--"
<span class="hljs-comment"># Boolean check — compare response lengths
curl -s <span class="hljs-string">"https://staging.example.com/api/product?id=1 AND 1=1" <span class="hljs-pipe">| <span class="hljs-built_in">wc -c
curl -s <span class="hljs-string">"https://staging.example.com/api/product?id=1 AND 1=2" <span class="hljs-pipe">| <span class="hljs-built_in">wc -cIf the time delay appears or lengths differ, the parameter is likely injectable.
Tamper Scripts
WAFs (Web Application Firewalls) block obvious injection payloads. Tamper scripts transform payloads to bypass signature-based detection.
Built-In Tamper Scripts
# Space-to-comment: replaces spaces with /**/
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/users?id=1" \
--tamper=space2comment
<span class="hljs-comment"># Case randomization: rAnDoMcAsE SQL keywords
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/users?id=1" \
--tamper=randomcase
<span class="hljs-comment"># URL encoding
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/users?id=1" \
--tamper=percentage
<span class="hljs-comment"># Multiple tampers — applied in order
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/users?id=1" \
--tamper=space2comment,randomcase,betweenCommon WAF bypass combinations:
# For ModSecurity
--tamper=space2comment,between,randomcase
<span class="hljs-comment"># For Cloudflare
--tamper=space2randomblank,randomcase,charencode
<span class="hljs-comment"># For AWS WAF
--tamper=space2comment,overlongutf8safe,charunicodeencodeWriting Custom Tamper Scripts
Create custom_tamper.py:
#!/usr/bin/env python3
from lib.core.enums import PRIORITY
__priority__ = PRIORITY.NORMAL
def dependencies():
pass
def tamper(payload, **kwargs):
"""
Replaces spaces with URL-encoded tab characters and
adds inline comments between keywords.
Example:
Input: SELECT * FROM users WHERE id=1
Output: SELECT%09*%09FROM%09users%09WHERE%09id=1
"""
if payload:
# Replace spaces with tab (URL-encoded)
payload = payload.replace(' ', '%09')
# Add comment between FROM and table name
payload = payload.replace('FROM%09', 'FROM/**/%09')
return payloadUse it:
sqlmap -u "https://staging.example.com/api/users?id=1" \
--tamper=/path/to/custom_tamper.pyTesting Against Your WAF
To validate your WAF is blocking real payloads:
# Use --safe-url to send legitimate requests between attack requests
<span class="hljs-comment"># WAFs often reset rate limits after seeing normal traffic
sqlmap -u <span class="hljs-string">"https://staging.example.com/api/users?id=1" \
--safe-url=<span class="hljs-string">"https://staging.example.com/api/health" \
--safe-freq=3 \
--tamper=space2comment,randomcase \
--level=3 --risk=2CI Pipeline Integration
GitHub Actions
name: SQL Injection Scan
on:
pull_request:
branches: [main]
paths:
- 'src/api/**'
- 'src/db/**'
jobs:
sqlmap-scan:
runs-on: ubuntu-latest
services:
db:
image: postgres:16
env:
POSTGRES_PASSWORD: testpass
POSTGRES_DB: testdb
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
app:
image: your-app:latest
env:
DATABASE_URL: postgresql://postgres:testpass@db:5432/testdb
ports:
- 8000:8000
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Wait for app
run: |
timeout 60 bash -c 'until curl -f http://localhost:8000/health; do sleep 2; done'
- name: Create test session
id: auth
run: |
TOKEN=$(curl -s -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"test@example.com","password":"TestPass123!"}' \
| jq -r '.token')
echo "token=$TOKEN" >> $GITHUB_OUTPUT
- name: Run SQLMap scan
run: |
docker run --network host \
-v $(pwd)/sqlmap-output:/output \
ghcr.io/sqlmapproject/sqlmap \
-u "http://localhost:8000/api/products?id=1" \
-H "Authorization: Bearer ${{ steps.auth.outputs.token }}" \
--batch \
--level=2 \
--risk=1 \
--output-dir=/output \
--forms \
--crawl=2 \
--exclude-sysdbs
- name: Check for injections found
run: |
if grep -r "sqlmap identified" sqlmap-output/ 2>/dev/null; then
echo "::error::SQL injection vulnerabilities found!"
exit 1
fi
echo "No SQL injection vulnerabilities found."
- name: Upload SQLMap output
uses: actions/upload-artifact@v4
if: always()
with:
name: sqlmap-results
path: sqlmap-output/Scripted Scan with Exit Code
For simpler pipelines:
#!/bin/bash
<span class="hljs-comment"># scan.sh — exits 1 if injection found, 0 if clean
TARGET=<span class="hljs-string">"${1:-http://localhost:8000}"
AUTH_TOKEN=<span class="hljs-string">"${2:-}"
ARGS=(
-u <span class="hljs-string">"$TARGET/api/products?id=1"
--batch
--level=2
--risk=1
--exclude-sysdbs
--output-dir=/tmp/sqlmap-output
)
<span class="hljs-keyword">if [ -n <span class="hljs-string">"$AUTH_TOKEN" ]; <span class="hljs-keyword">then
ARGS+=(-H <span class="hljs-string">"Authorization: Bearer $AUTH_TOKEN")
<span class="hljs-keyword">fi
sqlmap <span class="hljs-string">"${ARGS[@]}"
<span class="hljs-keyword">if grep -r <span class="hljs-string">"sqlmap identified" /tmp/sqlmap-output/ 2>/dev/null; <span class="hljs-keyword">then
<span class="hljs-built_in">echo <span class="hljs-string">"FAIL: SQL injection vulnerability found"
<span class="hljs-built_in">exit 1
<span class="hljs-keyword">fi
<span class="hljs-built_in">echo <span class="hljs-string">"PASS: No SQL injection found"
<span class="hljs-built_in">exit 0Testing JSON and GraphQL Endpoints
Modern APIs often use JSON bodies or GraphQL. SQLMap handles these with the right configuration.
JSON API
sqlmap -u "https://staging.example.com/api/search" \
--data=<span class="hljs-string">'{"query":"test","page":1}' \
-H <span class="hljs-string">"Content-Type: application/json" \
-H <span class="hljs-string">"Authorization: Bearer TOKEN" \
--level=3 \
--batchSQLMap automatically tests each JSON field.
GraphQL
# Test a GraphQL query variable
sqlmap -u <span class="hljs-string">"https://staging.example.com/graphql" \
--data=<span class="hljs-string">'{"query":"{ user(id: \"*\") { name email } }"}' \
-H <span class="hljs-string">"Content-Type: application/json" \
--level=3 \
--batchThe * marks the injection point.
Interpreting Results
Vulnerability Confirmed
---
Parameter: id (GET)
Type: boolean-based blind
Title: AND boolean-based blind - WHERE or HAVING clause
Payload: id=1 AND 4892=4892
Type: time-based blind
Title: MySQL >= 5.0.12 AND time-based blind
Payload: id=1 AND SLEEP(5)
---This is a confirmed injectable parameter. The application must be fixed before deploying.
No Injection Found
[INFO] the back-end DBMS is unknown
[WARNING] GET parameter 'id' does not seem to be injectable
[CRITICAL] all tested parameters appear to be not injectable.This is the expected result for safe endpoints. Note: a clean result at --level=2 --risk=1 doesn't mean the endpoint is bulletproof — deeper testing at higher levels may still find issues.
Setting Level and Risk
--level 1-5: Controls which parameters are tested (cookies, Referer, User-Agent at higher levels)--risk 1-3: Controls how dangerous the payloads are (heavy time-based, OR-based at higher risk)
For CI pipelines: --level=2 --risk=1 — thorough enough for most endpoints, won't trash the database. For manual security testing: --level=3 --risk=2 — finds more, but run against a test database only.
Responsible Use
SQLMap is a legitimate security testing tool. Use it only against:
- Systems you own
- Systems where you have explicit written authorization
- Test/staging environments — never production unless required by a pentest scope and you understand the risks
In CI/CD, always run against a dedicated test database that can be restored. Even --risk=1 payloads can corrupt data in edge cases.