SQLMap Testing Guide: SQL Injection Detection, Tamper Scripts & CI Integration

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 query

Test 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=2

The * 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=3

SQLMap 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 -c

If 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,between

Common 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,charunicodeencode

Writing 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 payload

Use it:

sqlmap -u "https://staging.example.com/api/users?id=1" \
  --tamper=/path/to/custom_tamper.py

Testing 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=2

CI 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 0

Testing 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 \
  --batch

SQLMap 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 \
  --batch

The * 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.

Read more