SQL Injection Testing
SQL Injection is consistently the most critical and prevalent web application vulnerability. It occurs when user-supplied input is directly incorporated into SQL queries without sanitization. As a tester, your job is to verify that ALL input points are protected — not to exploit the database, but to confirm the protection exists.
SQL Injection Test Vectors
// ══════════════════════════════════════════════════════════════
// SQL INJECTION TEST PAYLOADS (for testing — not for attacking)
// Test these in login forms, search fields, URL parameters, APIs
// ══════════════════════════════════════════════════════════════
const sqlInjectionTestPayloads = [
// Basic authentication bypass attempts:
"' OR '1'='1", // Classic auth bypass
"' OR '1'='1' --", // With comment to ignore rest of query
"' OR 1=1 --",
"admin' --", // Try to login as admin with any password
"' OR 'x'='x",
// Error-based detection (these cause SQL errors in vulnerable apps):
"'", // Single quote — syntax error in vulnerable app
"''",
"1' AND '1'='1",
// Time-based blind injection (detects vulnerability via delay):
"1'; WAITFOR DELAY '0:0:5'--", // SQL Server — causes 5s delay if vulnerable
"1'; SELECT pg_sleep(5) --", // PostgreSQL
"1' AND SLEEP(5) --", // MySQL
// NoSQL injection (for MongoDB, etc.):
'{"$gt": ""}', // MongoDB operator injection
'{"username": {"$ne": ""}}', // Authentication bypass
];
// ══════════════════════════════════════════════════════════════
// WHAT TO TEST — EVERY INPUT POINT IS A POTENTIAL VULNERABILITY
// ══════════════════════════════════════════════════════════════
const inputPointsToTest = [
"Login form: email and password fields",
"Search bar: all search inputs",
"URL parameters: GET /products?id=1",
"API JSON body fields: { 'name': '<payload>' }",
"HTTP headers: X-User-ID, Cookie values",
"File upload: filename parameter",
"Sort/filter parameters: ?sort=name&direction=asc",
];
// ══════════════════════════════════════════════════════════════
// HOW TO DETECT SQL INJECTION VULNERABILITY (as a tester)
// ══════════════════════════════════════════════════════════════
// VULNERABLE response patterns (any of these = SQL injection exists):
const vulnerablePatterns = [
"SQL syntax error in response body", // Database error exposed
"ORA-01756: quoted string", // Oracle error
"mysql_fetch_array() expects", // MySQL PHP error
"SQLSTATE[42000]", // PDO SQL error
"Unclosed quotation mark", // SQL Server error
"Login succeeds with payload", // Auth bypass succeeded
"Response delayed 5 seconds", // Time-based detection
];
// SECURE response (what you WANT to see):
const securePatterns = [
"Generic error: 'Invalid credentials'", // No details about why
"400 Bad Request with generic message", // Input rejected
"Response same as normal wrong password", // No timing difference
"Login fails despite payload", // Auth bypass not possible
];
// TESTING WITH PYTHON REQUESTS:
import requests
def test_sql_injection_in_login():
payloads = ["' OR '1'='1", "admin'--", "' OR 1=1 --"]
for payload in payloads:
response = requests.post("https://staging.myapp.com/api/auth/login", json={
"email": payload,
"password": "anything"
})
body = response.text
status = response.status_code
# FAIL if login succeeded with injection payload
assert status != 200, f"SQL injection bypass with payload: {payload}"
# FAIL if SQL error is exposed
sql_errors = ["sql", "syntax", "sqlite", "mysql", "oracle", "SQLSTATE"]
body_lower = body.lower()
for error_keyword in sql_errors:
assert error_keyword not in body_lower, f"SQL error exposed in response with payload '{payload}'"Common Mistakes
- Only testing the login form — every input that reaches a database query is a SQL injection risk; test search, filters, URL parameters, and API bodies
- Treating no error response as 'not vulnerable' — time-based blind SQL injection can succeed silently; check for response time anomalies with SLEEP() payloads
- Not encoding payloads for URL parameters — URL parameters require URL encoding; use requests library which handles this automatically
- Performing SQL injection tests in production — always test in staging; a careless time-based SQLi test can lock tables and take down a production database
Tip
Tip
Practice SQL Injection Testing in small, isolated examples before integrating into larger projects. Breaking concepts into small experiments builds genuine understanding faster than reading alone.
Fix A01-A03 to prevent 60%+ of web attacks
Practice Task
Note
Practice Task — (1) Write a working example of SQL Injection Testing from scratch without looking at notes. (2) Modify it to handle an edge case (empty input, null value, or error state). (3) Share your solution in the Priygop community for feedback.
Quick Quiz
Common Mistake
Warning
A common mistake with SQL Injection Testing is skipping edge case testing — empty inputs, null values, and unexpected data types. Always validate boundary conditions to write robust, production-ready software testing code.
Key Takeaways
- SQL Injection is consistently the most critical and prevalent web application vulnerability.
- Only testing the login form — every input that reaches a database query is a SQL injection risk; test search, filters, URL parameters, and API bodies
- Treating no error response as 'not vulnerable' — time-based blind SQL injection can succeed silently; check for response time anomalies with SLEEP() payloads
- Not encoding payloads for URL parameters — URL parameters require URL encoding; use requests library which handles this automatically