Skip to main content

Free 30-min security demo  — We'll scan your real code and show live findings, no commitment Book Now

Offensive360
Security Best Practices

API Security Checklist: 30 Controls to Test Before You Ship (2026)

A practical API security checklist covering authentication, authorization, rate limiting, input validation, CORS, logging, and OWASP API Top 10 — with pass/fail criteria.

Offensive360 Security Research Team — min read
api security checklist api security best practices OWASP API security API security audit REST API security API testing api security controls api security audit checklist api protection api security guidelines

Shipping an insecure API is one of the most common and costly mistakes in modern software development. APIs are the backbone of every web and mobile application, and they are the primary attack surface in most data breaches today. The OWASP API Security Top 10 documents the most exploited risks — but knowing the list and actually checking your implementation are two different things.

This checklist gives you 30 concrete pass/fail controls to verify before an API goes to production. Use it during development, code review, security testing, or a pre-launch security audit.


How to Use This Checklist

Each control includes:

  • What to check — the specific behavior to verify
  • How to test it — what to look for in code or with a testing tool
  • OWASP API Top 10 mapping — the risk category it addresses

Run through these controls against every new API surface and after any significant change to authentication, authorization, or data-handling logic.


Section 1: Authentication

Authentication controls verify that every request comes from an identified party.

✅ 1. Every endpoint requires authentication (unless intentionally public)

What to check: No sensitive endpoint is accessible without a valid token or session.

How to test: Send a request to each endpoint without any Authorization header or session cookie. Expect a 401 Unauthorized response — not a 200 with data.

# Quick test — should return 401, not data
curl -s https://api.yourapp.com/users/me

OWASP mapping: API2 — Broken Authentication


✅ 2. JWTs are validated with an explicit algorithm

What to check: JWT verification specifies the algorithm. alg: none attacks are blocked.

How to test: Decode a valid JWT, change the alg header to none, remove the signature, and send it. The server must reject it.

// SECURE — explicit algorithm prevents confusion attacks
jwt.verify(token, secret, { algorithms: ['HS256'] });

// VULNERABLE — allows 'none' attack
jwt.verify(token, secret); // Never do this

OWASP mapping: API2 — Broken Authentication


✅ 3. JWT expiration (exp) is enforced

What to check: Tokens expire. Expired tokens are rejected.

How to test: Capture a token, wait for expiration (or manually edit the exp claim to a past timestamp), and replay the request. Expect 401.

OWASP mapping: API2 — Broken Authentication


✅ 4. API keys are stored and compared securely

What to check: API keys are compared using constant-time comparison (not ==). Keys are hashed at rest, not stored in plaintext.

How to test: Review the source code for API key validation logic.

# SECURE — constant-time comparison prevents timing attacks
import hmac
if not hmac.compare_digest(provided_key, expected_key):
    return 401

OWASP mapping: API2 — Broken Authentication


✅ 5. Credentials are never logged or returned in responses

What to check: Passwords, tokens, and API keys are never written to logs or included in API responses.

How to test: Trigger authentication requests and inspect logs and response bodies for credential values.

OWASP mapping: API8 — Security Misconfiguration


Section 2: Authorization

Authorization controls verify that authenticated users can only access what they’re allowed to.

✅ 6. Object-level authorization on every resource endpoint

What to check: Accessing /api/orders/123 requires that order 123 belongs to the requesting user.

How to test: Log in as User A, capture an object ID (order, document, account). Log in as User B and attempt to access User A’s object by ID. Expect 403 Forbidden or 404 Not Found.

# SECURE — ownership check
order = Order.query.filter_by(id=order_id, user_id=current_user.id).first_or_404()

OWASP mapping: API1 — Broken Object Level Authorization (BOLA/IDOR)


✅ 7. Function-level authorization for admin endpoints

What to check: Admin-only endpoints (/api/admin/*, user deletion, privilege changes) are inaccessible to regular users even if they know the URL.

How to test: Attempt to call admin-level endpoints with a standard user token. Expect 403.

OWASP mapping: API5 — Broken Function Level Authorization


✅ 8. Mass assignment protection is in place

What to check: Users cannot supply extra fields (like is_admin, account_balance) to update privileged properties.

How to test: Submit a PUT/PATCH request with additional fields beyond what the API advertises. Verify they are ignored.

# SECURE — explicit allowlist of updatable fields
ALLOWED_FIELDS = {'name', 'bio', 'avatar_url'}
safe_data = {k: v for k, v in request.json.items() if k in ALLOWED_FIELDS}

OWASP mapping: API3 — Broken Object Property Level Authorization


✅ 9. Role permissions are enforced server-side

What to check: Role changes or permission checks are never based on client-supplied values.

How to test: Modify role claims in requests (e.g., change role: "user" to role: "admin" in a JWT payload). Verify the server ignores or rejects the tampered value.

OWASP mapping: API5 — Broken Function Level Authorization


Section 3: Input Validation

✅ 10. All request parameters are validated by type, length, and format

What to check: Every input field has explicit validation rules: type, minimum/maximum length, allowed characters, allowed values.

How to test: Submit unexpected types (string where integer expected), oversized values, special characters. Expect 400 Bad Request with a validation error — not a 500.

// Zod validation (TypeScript)
const schema = z.object({
  email: z.string().email().max(255),
  age: z.number().int().min(0).max(150),
  role: z.enum(['user', 'viewer']), // Whitelist
});

OWASP mapping: API4 — Unrestricted Resource Consumption


✅ 11. File uploads are validated for type and size

What to check: Accepted file types are explicitly allowlisted. File size is capped. Files are scanned for malicious content where applicable.

How to test: Upload an executable disguised as an image (e.g., a PHP webshell with a .jpg extension). Verify it is rejected or safely stored without execute permissions.

OWASP mapping: API8 — Security Misconfiguration


✅ 12. SQL injection prevention via parameterized queries

What to check: All database queries use prepared statements or an ORM. No string concatenation of user input into SQL.

How to test: Submit SQL metacharacters (', ", --, ;) in every input field. Check for database errors or unexpected behavior. Use a SAST scanner to trace data flows from input to query.

OWASP mapping: API1 / A03 Injection


✅ 13. XML input is processed with external entity expansion disabled

What to check: XML parsers are configured to reject external entity references (XXE).

How to test: Submit an XML payload with an external entity reference (<!DOCTYPE foo [<!ENTITY xxe SYSTEM "file:///etc/passwd">]>). The response should not include file contents.

OWASP mapping: API8 — Security Misconfiguration


Section 4: Rate Limiting and Resource Controls

✅ 14. Authentication endpoints have strict rate limits

What to check: Login, registration, password reset, and token refresh endpoints limit requests per IP.

How to test: Send 20+ requests in quick succession to /auth/login. Expect rate-limit responses (429 Too Many Requests) after the threshold.

OWASP mapping: API4 — Unrestricted Resource Consumption


✅ 15. All API endpoints have rate limits

What to check: Even non-sensitive endpoints have rate limits to prevent scraping and abuse.

How to test: Send 200+ requests per minute to a general API endpoint. Verify rate limiting kicks in.

OWASP mapping: API4 — Unrestricted Resource Consumption


✅ 16. Response payloads are size-capped

What to check: Endpoints that return lists implement pagination. There is no unbounded response that can return millions of rows.

How to test: Request data without pagination parameters. Verify the response is paginated and capped at a reasonable limit (e.g., 100 items).

OWASP mapping: API4 — Unrestricted Resource Consumption


✅ 17. GraphQL query depth and complexity limits are enforced

What to check (GraphQL APIs only): Deeply nested queries and expensive query patterns are rejected before execution.

How to test: Submit a deeply nested query (e.g., 10+ levels deep). Verify the server rejects it with a complexity or depth limit error.

validationRules: [depthLimit(5), costAnalysis({ maximumCost: 1000 })]

OWASP mapping: API4 — Unrestricted Resource Consumption


Section 5: Transport Security

✅ 18. All traffic is HTTPS only

What to check: HTTP requests are redirected to HTTPS. No API endpoint is accessible over plain HTTP.

How to test: Send a request using http:// instead of https://. Expect a 301 redirect or connection refusal — not a 200 response.

OWASP mapping: API8 — Security Misconfiguration


✅ 19. TLS 1.2 minimum is enforced

What to check: SSL 2.0, SSL 3.0, TLS 1.0, and TLS 1.1 are disabled.

How to test: Use testssl.sh or SSL Labs API to check supported protocol versions.

testssl.sh --protocols api.yourapp.com

OWASP mapping: API8 — Security Misconfiguration


✅ 20. HSTS header is present

What to check: The Strict-Transport-Security header instructs browsers never to downgrade to HTTP.

How to test: Inspect response headers. Look for Strict-Transport-Security: max-age=31536000; includeSubDomains.

OWASP mapping: API8 — Security Misconfiguration


Section 6: CORS and Headers

✅ 21. CORS is configured with an explicit origin allowlist

What to check: Access-Control-Allow-Origin is not set to * for endpoints that accept credentials or return sensitive data.

How to test: Send a request with an Origin: https://evil.com header. Verify the response does not reflect it back or include wildcard CORS headers for credentialed requests.

OWASP mapping: API8 — Security Misconfiguration


✅ 22. Security headers are present on all responses

What to check: X-Content-Type-Options: nosniff, Cache-Control: no-store (for sensitive endpoints), and X-Frame-Options: DENY are set.

How to test: Inspect response headers for all API responses.

OWASP mapping: API8 — Security Misconfiguration


✅ 23. Server version information is not disclosed

What to check: Server, X-Powered-By, and framework version headers are removed from responses.

How to test: Inspect response headers for Server: nginx/1.18.0, X-Powered-By: Express, or similar.

OWASP mapping: API8 — Security Misconfiguration


Section 7: Sensitive Data Exposure

✅ 24. API responses contain only necessary data

What to check: Responses do not include password hashes, internal user flags, system metadata, or any field not explicitly required by the client.

How to test: Review the full response body for every endpoint. Look for fields like password_hash, is_admin, internal_notes, SSNs, or payment card numbers.

OWASP mapping: API3 — Broken Object Property Level Authorization


✅ 25. Error messages do not reveal implementation details

What to check: Error responses do not include stack traces, database schema information, file paths, or framework names.

How to test: Trigger validation errors and server errors (malformed JSON, missing required fields, invalid IDs). Verify error messages are generic at the API level.

OWASP mapping: API8 — Security Misconfiguration


✅ 26. PII is handled according to data minimization principles

What to check: Personally identifiable information is not stored or returned unless strictly necessary. PII in transit is encrypted; PII at rest is appropriately protected.

OWASP mapping: API3 / General compliance


Section 8: Logging and Monitoring

✅ 27. Authentication events are logged with context

What to check: Every login success, login failure, password reset, and token issuance is logged with timestamp, IP address, user agent, and user ID.

How to test: Trigger authentication events and verify they appear in your logging system with sufficient context to investigate incidents.

OWASP mapping: A09:2021 — Security Logging and Monitoring Failures


✅ 28. Authorization failures are logged

What to check: Every 403 Forbidden response is logged. Patterns of repeated authorization failures from the same IP or user trigger alerts.

How to test: Deliberately trigger authorization failures and verify they are captured in logs and monitored.

OWASP mapping: A09:2021 — Security Logging and Monitoring Failures


✅ 29. Anomalous data volume requests trigger alerts

What to check: Requests that return unusually large data volumes (potential mass data extraction) are flagged for review.

OWASP mapping: API4 — Unrestricted Resource Consumption / A09 Logging Failures


Section 9: Inventory and Lifecycle

✅ 30. All API endpoints are documented and inventoried

What to check: Every deployed API endpoint is in your API inventory. Deprecated and legacy endpoints are explicitly tracked and have a decommission plan.

How to test: Compare your API documentation against actual deployed routes. Use a DAST scanner to discover undocumented endpoints.

OWASP mapping: API9 — Improper Inventory Management


OWASP API Security Top 10 Coverage Summary

OWASP RiskControls in This Checklist
API1 — Broken Object Level Authorization#6, #12
API2 — Broken Authentication#1, #2, #3, #4, #5
API3 — Broken Object Property Level Authorization#8, #24, #26
API4 — Unrestricted Resource Consumption#10, #14, #15, #16, #17, #29
API5 — Broken Function Level Authorization#7, #9
API6 — Unrestricted Access to Sensitive Business Flows#14, #15
API7 — Server-Side Request Forgery#10, #12
API8 — Security Misconfiguration#5, #11, #13, #18, #19, #20, #21, #22, #23, #25
API9 — Improper Inventory Management#30
API10 — Unsafe Consumption of APIs#10, #13

How to Automate This Checklist

SAST — Code-Level Checks

A code vulnerability scanner like Offensive360 SAST can automatically verify many of these controls at the source code level:

  • Authentication checks — detecting endpoints missing authentication middleware
  • SQL injection — taint analysis tracing input to query construction
  • Mass assignment — detecting models bound directly to request bodies
  • Hardcoded credentials — API keys and secrets in source code
  • Insecure cryptography — weak JWT configurations, MD5 usage

DAST — Runtime Checks

Dynamic API testing with Offensive360 DAST verifies runtime behavior:

  • BOLA/IDOR — automated object-level authorization testing across endpoints
  • Rate limiting — verifying throttling is actually enforced
  • CORS misconfiguration — testing with arbitrary Origin headers
  • Error disclosure — inducing errors and inspecting responses
  • Authentication bypass — testing missing and invalid token handling

Run This Checklist on Your API

Manual checklist reviews are useful, but automated SAST + DAST scanning catches issues that manual review misses, runs in CI/CD on every change, and scales across large API surfaces.

Offensive360 Security Research Team

Application Security Research

Find vulnerabilities before attackers do

Run Offensive360 SAST and DAST against your applications and get a full vulnerability report in minutes.