API Security Testing Beyond Status 200: What Automated Scanners Actually Check
Apr 14, 2026 · 7 min read
Your API returns 200 OK. Your integration tests pass. Your CI pipeline is green. None of that tells you whether an attacker can enumerate every user record in your database by incrementing an ID in the URL, or whether your JWT validation actually rejects expired tokens instead of silently accepting them.
API security testing operates in a fundamentally different space than functional testing. A functionally correct endpoint can be completely insecure. The response code tells you the server processed the request — it says nothing about whether the request should have been allowed in the first place.
This is what automated API security scanners actually probe for, where they hit their limits, and what still requires a human with a proxy tool and bad intentions.
The OWASP API Security Top 10: What Actually Gets Exploited
OWASP published its API-specific top 10 list because web application vulnerabilities and API vulnerabilities overlap but are not the same. APIs expose raw data and business logic directly, without a browser UI to constrain what gets sent. Three categories dominate real-world breaches.
Broken Object Level Authorization (BOLA)
BOLA is the single most exploited API vulnerability, responsible for breaches at companies ranging from ride-sharing platforms to financial services. The pattern is deceptively simple: your API uses an object identifier in the URL or request body (GET /api/v1/orders/12345), and the server returns the object without verifying that the authenticated user is authorized to access it.
Automated scanners test for BOLA by authenticating as User A, collecting valid object IDs from User A's responses, then replaying those requests with User B's credentials — or with IDs modified by incrementing, decrementing, or substituting known patterns. If the server returns a 200 with data that belongs to another user, you have a BOLA vulnerability.
The subtlety that trips teams up: BOLA is not just about sequential integer IDs. UUIDs reduce guessability but do not eliminate BOLA. If any endpoint leaks object IDs — search results, shared links, WebSocket messages, error messages containing references — those UUIDs become valid attack material. Authorization checks must happen on every request, regardless of ID format.
Broken Authentication
Authentication vulnerabilities in APIs are different from web login-page attacks. Scanners probe for a specific set of weaknesses:
-
Expired token acceptance:
Sending a JWT with an
expclaim in the past. A surprising number of APIs accept these because the validation library is configured to skip expiration checks, or because the server clock is not synchronized. -
Algorithm confusion:
Sending a JWT with
alg: noneor switching from RS256 to HS256 to see if the server accepts the public key as an HMAC secret. This is CVE-2015-9235 and it still works against poorly configured JWT libraries. - Missing authentication: Requesting protected endpoints with no Authorization header at all. Some APIs only check authentication when the header is present and default to an anonymous but elevated context when it is absent.
- Credential stuffing resistance: Sending multiple failed login attempts to check whether the API implements rate limiting, account lockout, or CAPTCHA challenges after repeated failures.
- Token leakage: Checking whether tokens appear in URL query parameters (which end up in server logs, browser history, and referrer headers) instead of Authorization headers or secure cookies.
Excessive Data Exposure
This is the vulnerability you create when you return an entire database row as JSON and rely on the frontend to display only the fields the user should see. Scanners detect it by comparing response payloads against a schema of expected fields. If your
/api/users/me
endpoint returns password_hash, ssn, internal_role, or
stripe_customer_id
alongside the name and email, that is excessive data exposure — even if the frontend never renders those fields.
The fix is straightforward but requires discipline: every API response should go through a serializer or view layer that explicitly whitelists fields. Never pass ORM objects directly to JSON encoding.
Rate Limiting: How Scanners Probe for Missing or Bypassable Limits
Rate limiting is not just about preventing denial-of-service. Without rate limits, every other vulnerability becomes easier to exploit — brute-force attacks against authentication, BOLA enumeration across millions of IDs, and data scraping all depend on being able to send thousands of requests without being throttled.
Automated scanners test rate limiting in several phases:
- Baseline detection: Send a burst of identical requests (typically 50-100 in rapid succession) and check whether the server ever returns 429 Too Many Requests. If every request returns 200, rate limiting is either absent or set too high to be effective.
-
Header inspection:
Check for
X-RateLimit-Limit,X-RateLimit-Remaining, andX-RateLimit-Resetheaders. Their absence does not mean rate limiting is missing, but their presence reveals the exact thresholds an attacker needs to stay just under. -
Bypass techniques:
Rotate the
X-Forwarded-Forheader with different IPs, switch between API key and session cookie auth, or vary query parameters to see if the rate limiter tracks by IP, by user, by endpoint, or some combination. Many rate limiters only track by source IP, meaning an attacker behind a rotating proxy pool is effectively unlimited. -
Per-endpoint consistency:
Test whether rate limits apply uniformly. A common gap is applying strict limits to
/loginbut none to/api/v1/password-resetor/api/v1/users?email=.
Production-grade rate limiting should be layered: a global limit at the reverse proxy or API gateway (NGINX, Kong, AWS API Gateway), and per-endpoint limits in application code for sensitive operations like authentication, password reset, and data export.
CORS Misconfiguration Detection
Cross-Origin Resource Sharing misconfigurations are among the easiest vulnerabilities to detect automatically and among the most commonly found. The scanner sends requests with an
Origin
header set to various attacker-controlled domains and inspects the response headers.
The specific checks:
-
Wildcard with credentials:
If the API returns
Access-Control-Allow-Origin: *alongsideAccess-Control-Allow-Credentials: true, browsers will actually block this (the spec forbids it), but the configuration reveals the developer intended to allow all origins with credentials — which means the actual implementation is likely just reflecting the Origin header back, which is worse. -
Origin reflection:
The server echoes back whatever Origin header it receives as the
Access-Control-Allow-Originvalue. This effectively disables the same-origin policy for your API. An attacker's page atevil.comcan make authenticated requests and read the responses. -
Subdomain matching flaws:
If the server allows
*.example.com, a scanner tests whetherattacker-example.comalso passes. Regex-based origin validation is notorious for this — the patternexample\.comwithout anchoring matchesnotexample.com. -
Null origin acceptance:
Some APIs whitelist the
nullorigin, which can be triggered from sandboxed iframes and data URIs — both attacker-controllable.
The correct CORS configuration for most APIs is an explicit allowlist of specific origins (your frontend domains), with
Access-Control-Allow-Credentials: true
only when the API uses cookies for authentication. If the API uses Bearer tokens exclusively, credentials support is unnecessary and should be disabled.
Authentication Testing Patterns in Detail
Beyond the basic checks covered in the OWASP section, sophisticated API scanners run a matrix of authentication edge cases that catch real bugs in production:
| Test | What it checks | Common failure |
|---|---|---|
| Token after logout | Does the server invalidate tokens on logout? | JWTs remain valid until expiry since they are stateless by default |
| Role downgrade | After removing admin role, does the old token still grant admin access? | Role cached in JWT claims, not re-checked against database |
| HTTP method override |
Can a GET request with
X-HTTP-Method-Override: DELETE
bypass authorization?
|
Middleware processes override before auth check |
| Path traversal in auth |
Does
/api/v1/admin/../user/profile
hit the admin auth middleware or the user auth middleware?
|
URL normalization happens after middleware routing |
| Privilege escalation via mass assignment |
Can a PUT request to
/api/users/me
include {"role": "admin"}?
|
No server-side field filtering on update |
Each of these tests requires the scanner to maintain authenticated sessions for multiple users with different permission levels. This is why API security scanning needs more configuration than a simple URL-and-go web scanner — you need to provide credentials, token generation logic, or OpenAPI specs with security scheme definitions.
What Automated Scanners Can and Cannot Catch
Automated API security scanners are good at finding a specific category of vulnerabilities: those that follow known patterns and can be detected by observing request-response behavior without understanding the business context.
Automated scanners reliably detect:
- Missing or misconfigured CORS headers
- Absent rate limiting on critical endpoints
- Excessive data exposure (extra fields in responses)
- Missing security headers (HSTS, X-Content-Type-Options, X-Frame-Options)
- Basic authentication bypasses (no-auth, expired tokens, algorithm confusion)
- SQL injection and NoSQL injection in query parameters and JSON bodies
- Server information disclosure (version headers, stack traces in error responses)
- SSRF via URL parameters (when the server fetches attacker-controlled URLs)
Automated scanners struggle with or miss entirely:
- Business logic flaws: A scanner cannot know that transferring negative amounts, applying a discount code twice, or approving your own expense report violates your business rules. These require human understanding of the application's purpose.
- Complex BOLA chains: When access to object A legitimately grants access to object B, but not to object C which is also linked to B, the authorization logic depends on business relationships that scanners cannot infer.
- Race conditions: TOCTOU (time-of-check-time-of-use) vulnerabilities in operations like balance checks, inventory reservation, or coupon redemption require precisely timed parallel requests with awareness of the expected behavior.
- Chained vulnerabilities: Low-severity findings that combine into critical exploits — for example, an IDOR that leaks email addresses, combined with a password reset flow that only requires an email, combined with a rate-limit gap on the reset endpoint.
- GraphQL-specific issues: Deep query nesting for denial-of-service, introspection query exposure, and batched query abuse require GraphQL-aware tooling that generic REST scanners do not provide.
The Manual vs Automated Testing Boundary
The practical approach is not choosing between manual and automated testing — it is using each where it has leverage.
Run automated scans on every deployment. They catch regressions immediately. A CORS header that was correct last week might be broken after a framework upgrade. Rate limiting that worked before a refactor might have disappeared. These are the kinds of things that slip through code review because they are configuration, not code.
Reserve manual testing for business logic, authorization models, and multi-step attack chains. A penetration tester with Burp Suite or OWASP ZAP in proxy mode can map your authorization model, identify the relationships between objects, and test whether the boundaries hold. This is expensive, which is why it should happen on a schedule (quarterly or after major architectural changes) rather than on every commit.
The gap between the two is where tools like Kuality fit: automated scanning that goes beyond "does this endpoint respond" to probe for the OWASP API Top 10 categories, test authentication edge cases, and flag CORS and rate-limiting gaps — continuously, on every deployment, without manual setup for each scan.
The goal is not to replace the penetration test. The goal is to make sure the penetration tester spends their time on business-logic vulnerabilities rather than finding the same missing rate limit that an automated scanner would have caught on day one.
Test your API endpoints for security vulnerabilities →