How to secure a REST API in 2026

Wed Aug 19 2026

But there is no OWASP API Security Top 10 for 2026. The current official edition remains OWASP API Security Top 10 2023. The year in this guide is the publication date and current guidance context, not a new OWASP release.

That 2023 list puts BOLA first. And it lists Broken Authentication, Broken Object Property Level Authorization, Unrestricted Resource Consumption, Broken Function Level Authorization, Security Misconfiguration and Improper Inventory Management among its risks. OWASP ranks risk. The 42Crunch State of API Security 2026 report reports flaws in a vendor-curated dataset. Those measurements answer different questions.

This guide turns REST API security best practices into controls you can test before release. And the request path moves from transport through identity, permission, input, abuse controls, data, observability and verification. Authorization and validation get the most attention because “use HTTPS” is easy to configure; deciding exactly who may change which object is where APIs tend to fail.

In this article

Put independent controls in the request path

A gateway can reject a token while your application still returns another tenant’s order. The controls must remain separate even when infrastructure places several of them in one service.

  1. TLS protects the connection.
  2. A gateway may handle routing, coarse token checks, or quotas.
  3. Authentication establishes the caller’s identity.
  4. Authorization decides whether that caller may use the endpoint and touch the object.
  5. Validation checks the input’s shape and meaning.
  6. Rate limiting controls resource consumption.
  7. The application returns only permitted data.
  8. Logging and monitoring record security signals.

This model describes the control flow rather than prescribing a software topology. So an edge service may rate-limit before authentication. Application code can apply a finer per-user limit later.

As Postman puts it, “No single control is enough.” And guides from Postman and StackHawk offer practical implementation advice. They are not standards. Use their advice to build controls, then adapt the policy to your application.

TLS protects the request before your code sees it

Configure transport security before reviewing route permissions. So credentials, tokens, and request data need protection before your application makes its first decision.

Use TLS 1.2 or higher, preferably TLS 1.3. Disable SSLv3, TLS 1.0, RC4, and 3DES. Use certificates from trusted certificate authorities, and make certificate expiry and replacement an owned operational task.

Add HSTS only when every covered subdomain supports HTTPS:

Strict-Transport-Security: max-age=31536000; includeSubDomains

Reject plain HTTP at the edge. A redirect still permits the initial request to travel unencrypted, so the cleartext route should fail.

For service-to-service traffic, assess mutual TLS. Both sides present certificates, giving internal services a stronger identity check than network location alone. Don’t add mTLS to every public client integration; certificate issuance and rotation create work that has to be owned.

Transport encryption covers data in motion. Stored data needs separate protection. Use the encryption standard required by your environment, including AES-256 where that is your policy, and manage keys through a KMS, Key Vault, or HSM. Cover the storage layers relevant to the data classification: Database fields and disks may need one set of controls. Replicas and backups may need another.

Keep sensitive values out of URLs. Proxies, web servers, analytics tools, and other infrastructure commonly log query strings.

Authenticate every request, then make a separate permission decision

OAuth 2.0 with JWT access tokens is one reasonable production pattern for delegated access and local token verification. JWT is an implementation choice, and security still depends on how you validate and use it. Use the issuer’s documented validation rules, and choose opaque tokens with introspection when central revocation is the dominant requirement.

For every protected request, validate the signature, expiration, issuer, audience, and required scopes or claims. Short-lived access tokens limit the exposure window; refresh tokens support longer sessions but need their own rotation and revocation policy. For high-risk scopes, consider introspection. A deny-list is another option. I can’t choose your revocation boundary from a generic guide. Write down how quickly access must be cut off, then verify it.

JWT advice is routinely cargo-culted. “We use JWT” says nothing until malformed, expired, wrongly signed, wrong-issuer, and wrong-audience tokens fail correctly.

Consider the multi-tenant order API:

GET /api/orders/123
Authorization: Bearer <access-token>

The token identifies the caller and may contain a user ID, tenant ID, and scopes. It does not grant access to order 123 by itself. The next layer must apply the tenant and resource policy.

API keys work for server-to-server integrations. Give each integration its own key, scope it, set an expiry, rotate it, and monitor its use. A key identifies the calling application; it cannot authorize a user’s access to another user’s order.

For browser clients, define a storage policy and verify that access tokens aren’t written to persistent browser storage without an explicit security design. The frontend is not a security boundary.

The 42Crunch report’s missing-authentication finding comes from vendor-curated 2025 cases; it does not measure a universal breach rate. My judgment is simpler: test missing authentication first because the failure is cheap to detect and expensive to excuse. A protected route without a token must return 401.

Authorization deserves more engineering than TLS configuration

OWASP lists Broken Object Level Authorization, or BOLA, as API1:2023. Predictable paths such as /api/orders/123 make systematic object probing easy.

The dangerous implementation checks only that the caller is logged in:

// WRONG: authentication exists, ownership does not
const order = await Order.findById(req.params.id);

For an API where tenant membership grants access to every order in that tenant, bind the lookup to the tenant:

// Correct only when tenant membership grants order access
const order = await Order.findOne({
  _id: req.params.id,
  tenant_id: req.user.tenant_id
});

That query is sufficient only if tenant membership is the complete policy. If access also depends on individual ownership, role, order state, or another business rule, include that predicate or policy check too. Test tenant membership and object ownership independently where both matter.

Suppose user A belongs to tenant North and user B belongs to tenant South. User A requesting /api/orders/123 must receive the documented non-disclosing 404 and no order data when order 123 belongs to South. A same-tenant user without permission must receive 403 if your contract distinguishes that case. Don’t choose 404 if clients need distinguishable authorization errors.

Repeat the test with:

PATCH /api/orders/123

The response must leave the record unchanged. A secure read does not imply a secure write.

Apply authorization at four levels:

  • Endpoint: may this identity call the route?
  • Object: may the caller access this order?
  • Property: may the caller change each submitted field?
  • Function: may this role perform an administrative operation?

A normal user might update a delivery note but never tenant_id, price, payment state, or an internal role. Explicit field allowlists prevent mass assignment. Use RBAC when permissions map cleanly to roles; use ABAC when decisions depend on tenant, ownership, region or order state.

Input validation must cover paths, queries, and bodies

The 42Crunch report identifies broken input validation, including injection, mass assignment, and path traversal, as the most common flaw category in its curated dataset. That is observed frequency, not OWASP’s risk ranking.

Define the accepted request shape and reject everything else. Validate required fields, types, lengths and ranges. Check formats, enum values and nested objects too. Validate path and query parameters. A schema system such as Joi, Zod, or JSON Schema keeps the rules in one place.

Use this boundary sequence:

  1. Confirm the method and Content-Type.
  2. Parse against a strict schema.
  3. Validate path and query parameters against allowlists.
  4. Enforce body-size and collection-size limits.
  5. Reject unknown fields before business logic or database access.

A JSON body is only one input channel. /api/orders/123, ?status=pending, headers, content types, and payload sizes are user-controlled too.

An input such as this should never become an unchecked database operator:

/api/users?status[$ne]=inactive

Allow only documented query keys and scalar values. The negative test should return 400, with no database query executed using the nested operator shape.

PATCH authorization belongs with the permission model above. The validation layer should enforce the resulting schema: reject fields the caller is not allowed to submit, rather than silently binding them to a database model. Invalid input should return 400 without stack traces, SQL fragments, or database internals.

Rate limits should follow endpoint cost

A single global rate limit is convenient but leaves endpoint-specific abuse costs unaddressed. Login and password reset have one abuse profile. Payments and writes have another. Read-only lists have different costs again.

  • Login and password reset: use per-IP plus per-account thresholds; return 429 and Retry-After after the documented limit.
  • Read-only order lists: use a higher per-user, per-tenant, or per-key limit; monitor for scraping.
  • Order writes: apply tighter per-user and per-tenant limits because writes consume more business capacity.
  • POST /api/payments: apply a strict client limit and require an idempotency key so retries cannot create duplicate charges.

Consider per-user, per-IP, per-tenant, and per-API-key dimensions where they fit your threat model. When a client exceeds a provider-defined window, return:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1700000000

Those values are illustrative. Document the actual window and identity dimension beside the policy.

I don’t know your legitimate traffic shape, so I won’t prescribe a universal number. Tune from telemetry. Overly aggressive limits create false positives; generous ones make expensive endpoints public utilities.

Keep secrets and responses smaller than the database

Never hardcode credentials, signing keys, database passwords, or API keys. Removing a secret from the current branch does not remove it from repository history.

Use environment variables for simple deployments and a dedicated secrets manager for managed access. Scope credentials to the smallest useful permission set, assign expirations and rotate after suspected exposure. Monitor usage for unusual volume or origin.

Authentication and authorization can succeed while the response leaks too much. Select fields explicitly. A client asking for a display name does not need password hashes, internal IDs, roles, tenant metadata, or the full user record.

Production errors should be generic and carry a request ID:

{
  "error": "request_failed",
  "request_id": "7f3c2a91"
}

Log detailed server-side context against that ID. Return the same outward failure behavior for “user not found” and “wrong password,” so callers cannot enumerate accounts.

Log signals without creating a second breach

Record successful and failed authentication, authorization failures and rate-limit violations. Also record sensitive-resource changes, unusual request volume and calls to nonexistent endpoints. Those events give an investigator a usable trail without requiring full request bodies.

Do not log passwords, access tokens, payment-card numbers, or complete sensitive request bodies. Redaction must happen before the event reaches storage.

Useful detection heuristics include:

  • a sharp rise in 401 responses may indicate credential stuffing;
  • a cluster of 500 responses around one endpoint may indicate an exploit attempt;
  • repeated 403 responses against neighboring object IDs may indicate authorization probing;
  • rate-limit violations concentrated on one key may indicate a compromised integration.

These signals need investigation rather than automatic conclusions. Alert owners need a way to inspect the request ID, route, safe principal identifier, and response status without exposing secrets.

Keep an inventory of documented APIs, versions, environments, and routes. Every deployed route needs an owner, authentication requirement, data classification, version, and retirement status. The inventory is how you implement the concern behind API9:2023: undocumented or stale routes must have an owner and a path to removal.

Measure token lifetimes, rate limits, and alert thresholds against your traffic. This guide can define controls; it cannot choose those operational values for you.

Prove the controls before release

Run negative tests in CI/CD, then repeat them during audits and independent penetration tests.

Authentication and authorization

  • A protected route without a token must return 401.
  • A valid token should reach the expected handler.
  • Expired, malformed, wrongly signed, wrong-issuer, and wrong-audience tokens must return 401.
  • An authenticated token without the required scope must return 403, unless the API documents a non-disclosing 404.
  • A request from user A for user B’s order must return the documented cross-tenant 404 and no data.
  • A same-tenant user without the required order permission must receive 403 when the contract distinguishes that case.
  • Reject user A’s PATCH /api/orders/123 request with forbidden fields and leave the record unchanged.

Validation and abuse

Send missing fields, invalid path and query values and oversized payloads. Test injection strings, unknown fields and unexpected content types. Expect 400 and no internal details.

Exceed each endpoint’s documented quota. Expect 429 with Retry-After. Repeat a payment request with one idempotency key and confirm that it creates one transaction.

Assert that secret-bearing log fields are redacted and production errors contain request IDs without stack traces. Automated scanners catch repeatable defects; they cannot infer your tenant and business rules. Add independent penetration testing when the API’s risk warrants it.

Use this API security checklist at release time

Mark every line pass, fail, or not applicable with a written reason. Attach an owner and test result.

  • Transport: TLS 1.2+, HSTS where all covered subdomains support HTTPS, and plain-HTTP rejection are verified; mTLS has an explicit service-to-service decision.
  • Authentication: Protected routes reject missing, expired, malformed, wrongly signed, wrong-issuer, and wrong-audience tokens with 401.
  • Token lifecycle: Access tokens are short-lived; refresh-token rotation and revocation behavior is documented and tested.
  • Authorization: Endpoint, function, object, tenant, and field checks are enforced in application code.
  • Input: Body, path, query, content type, unknown-field, and size checks return 400; rejected query operators never reach the database.
  • Abuse controls: Endpoint-specific limits use documented per-user, IP, tenant, or key dimensions; excess returns provider-defined 429 and Retry-After.
  • Transactions: Payment and transaction endpoints use idempotency keys and test duplicate-request behavior.
  • Stored data: Sensitive storage layers, including relevant backups and replicas, are encrypted; keys are managed through a KMS, Key Vault, or HSM.
  • Secrets: Repository and secret-scanning checks found no active credentials; historical findings are revoked, rotated, and recorded.
  • Responses: Only required fields return; production errors include request IDs and no stack traces or internal details.
  • Observability: Authentication, authorization and rate-limit events are logged without secrets. Record sensitive changes, anomalies and nonexistent routes too. Alert owners are assigned.
  • Inventory: Every deployed route has an owner, version, authentication requirement, data classification, and retirement status.
  • Verification: Security tests run in CI/CD, audits are scheduled, and penetration testing has an owner and date.

Release only when every line has pass, fail, or a written exception, plus an owner and test result. If the ticket says only “we use JWT” or “the gateway handles it,” send it back.