SSentinel Board
GuidesPricingOpen dashboard ↗

API SECURITY

API security testing checklist

Obsidian Orb InnovationsUpdated 9 September 2026~9 min read

An API has no UI to hide behind. Every rule you enforce in the browser or the mobile app can be skipped by a caller sending raw requests, so an API has to defend itself on its own terms. This checklist walks the OWASP-aligned checks that find the most real, exploitable bugs — with example requests you can run against a system you are authorized to test.

Run these only against an API you own or have explicit written permission to test. Sending injection payloads or access-control probes at systems you do not control is unauthorized access, and it is illegal.
THE CHECKS, IN PRIORITY ORDER
  1. Broken access control (BOLA)
  2. Missing or weak authentication
  3. Injection: SQL, command, and XSS
  4. Security headers
  5. CORS misconfiguration
  6. TLS and transport
  7. Information disclosure and rate limiting

1. Broken access control

This is number one on the OWASP list for a reason: it is the flaw attackers reach for first and the one that leaks the most data. The API returns objects based on an identifier in the request, but never checks whether the caller is allowed to see that object.

Horizontal: another user's data

Authenticate as a low-privilege user, request your own record, then increment the ID and replay with your own token:

# Your record — 200 expected
curl -H "Authorization: Bearer $TOKEN" \
  https://api.example.com/v1/users/1001

# Someone else's — this MUST NOT return their data
curl -H "Authorization: Bearer $TOKEN" \
  https://api.example.com/v1/users/1002

If the second call returns another user's data, that is broken object-level authorization (BOLA). Sequential integer IDs make it trivial to enumerate every record; UUIDs raise the effort but are not the fix — the fix is checking ownership on the server for every request.

Vertical: privileged actions

Take an admin-only route and call it with a regular user's token. Deletes, role changes, and "list all users" endpoints are the usual suspects. A 200 where you expected 403 is the finding.

2. Authentication

Two failures dominate here. The first is endpoints that simply forgot to require authentication — strip the Authorization header and replay; anything that still returns data is exposed. The second is weak token handling:

  • Expired tokens are actually rejected, not merely hidden by the client.
  • Tokens are validated server-side — a JWT with alg: none or an unverified signature is a critical bug.
  • There is a rate limit on login and password-reset, so credentials cannot be brute-forced.
  • Error messages do not reveal whether a username exists ("no such user" vs. "wrong password").

3. Injection

Injection happens whenever caller input reaches an interpreter — a SQL engine, a shell, an HTML renderer — without being safely separated from the code around it.

SQL injection

Send classic probes into any parameter that could reach a database and watch for the tell-tale signs:

GET /api/products?id=1'
GET /api/products?id=1 OR 1=1
GET /api/products?id=1; WAITFOR DELAY '0:0:5'--

A database error in the response, a set of records that should not match, or a five-second delay on the time-based payload each indicate the input is reaching SQL unescaped. Parameterized queries (prepared statements) are the fix — string concatenation is the cause.

Command injection

Any endpoint that touches the filesystem, generates a PDF, pings a host, or shells out is a candidate. Append shell metacharacters and look for command output in the response:

GET /api/export?file=report.csv;id
GET /api/ping?host=127.0.0.1|whoami

Reflected XSS

If an API echoes input into an HTML response (error pages and search endpoints are common), send a marker and check whether it comes back unescaped:

GET /api/search?q=<script>alert(1)</script>

The payload appearing verbatim in an text/html response means output encoding is missing.

4. Security headers

Headers are the cheapest hardening you will ever ship, and their absence is one of the most common findings in any scan. Check the response for:

  • Strict-Transport-Security — forces HTTPS on every future request.
  • X-Content-Type-Options: nosniff — stops MIME-type guessing.
  • X-Frame-Options or a frame-ancestors CSP — blocks clickjacking.
  • Content-Security-Policy — the single most effective control against XSS.

We wrote a separate guide on what each header does and how to set it, because "add security headers" is easy advice and surprisingly easy to get subtly wrong.

5. CORS

A permissive CORS policy lets a malicious website make authenticated requests to your API using a logged-in victim's browser. Inspect the response to a cross-origin request:

curl -i -H "Origin: https://evil.example" \
  https://api.example.com/v1/account

The dangerous combination is Access-Control-Allow-Origin reflecting any origin you send together with Access-Control-Allow-Credentials: true. That pairing lets any site read authenticated responses. Allow only the specific origins you control, and never reflect the caller's origin blindly.

6. TLS and transport

  • The API is reachable only over HTTPS; plain http:// either refuses or redirects.
  • The certificate is valid, unexpired, and covers the hostname in use.
  • No sensitive data — tokens, passwords, PII — travels in URL query strings, where it is logged at every hop.

7. Information disclosure and rate limiting

Finally, check what the API gives away for free. Trigger an error deliberately (malformed JSON, a wrong type, a missing field) and read the response: stack traces, framework version banners, SQL fragments, and internal hostnames all hand an attacker a map. Errors should be generic to the caller and detailed only in your server logs.

Then fire a burst of requests at an expensive endpoint and confirm the server starts returning 429 Too Many Requests. No rate limit means credential stuffing, scraping, and denial-of-wallet are all on the table.

Turning a checklist into a repeatable process

Running these once before launch is good. The real value is running them on every release and tracking what changed — which findings you fixed, which are still open, and whether a new endpoint quietly reintroduced an old class of bug. A checklist in a document does not do that; it just gets stale.

SENTINEL BOARD

Run this whole checklist as one command

Sentinel Board's scanner automates every check on this page — broken access control, authentication, SQL/command/XSS injection, security headers, CORS, TLS, and information disclosure — scores your overall posture, and exports a JSON report. Import it into the included local dashboard to see findings by severity and control coverage, and diff it against your last run. One-time $49, no subscription, and nothing ever leaves your machine.

See what's included →
← All guides Flutter app security testing Security headers explained
SSentinel Board
GuidesTermsPrivacyRefunds
© 2026 Obsidian Orb Innovations