close icon
daily.dev platform

Discover more from daily.dev

Personalized news feed, dev communities and search, much better than what’s out there. Maybe ;)

Start reading - Free forever
Start reading - Free forever
Continue reading >

The Developer Guide to API Security: OAuth 2.1, JWT Best Practices, and Common Vulnerabilities

The Developer Guide to API Security: OAuth 2.1, JWT Best Practices, and Common Vulnerabilities
Author
Nimrod Kramer
Related tags on daily.dev
toc
Table of contents
arrow-down

🎯

Practical API security guide covering OAuth 2.1 with PKCE, JWT signing and storage, authorization models, gateways, and testing tools.

APIs are the backbone of modern software, powering everything from apps to microservices. But as APIs now handle over 80% of web traffic (2026), they’ve become a prime target for attacks. Between 2021 and 2025, API attacks surged by 680%, with data breaches costing an average of $4.88 million.

The good news? Most API vulnerabilities, like broken access controls or injection flaws, stem from preventable issues. This guide dives into actionable strategies to secure your APIs, including:

  • Using OAuth 2.1 with PKCE for secure authentication.
  • Best practices for JWT (e.g., short-lived tokens, proper signing).
  • Addressing OWASP API Security Top 10 risks like BOLA, SSRF, and misconfigurations.
  • Implementing robust authorization models (RBAC, ABAC, scopes).
  • Leveraging tools like SAST scanners, DAST, and API scanners for testing.
API Security Statistics and Threat Landscape 2024-2026

API Security Statistics and Threat Landscape 2024-2026

API Authentication Explained 🔐 OAuth2, JWT, API Keys, SSO

OWASP API Security Top 10 (2023 Edition)

OWASP

The OWASP API Security Top 10 for 2023 provides an updated view of vulnerabilities affecting APIs. These vulnerabilities contribute to over 90% of API breaches, with 80% of attacks exploiting them.

This year's update introduced key revisions. For example, traditional injection attacks now fall under "Unsafe Consumption of APIs" (API10:2023), while Server-Side Request Forgery (SSRF) has been elevated to its own category (API7:2023). Additionally, "Excessive Data Exposure" and "Mass Assignment" have been merged into "Broken Object Property Level Authorization" (API3:2023), focusing on property-level authorization failures. A new entry, "Unrestricted Access to Sensitive Business Flows" (API6:2023), highlights risks like automated abuse of business logic - such as ticket scalping or mass commenting - that bypass traditional tools like WAFs.

Broken Object Level Authorization (BOLA)

Broken Object Level Authorization is the most frequent API vulnerability, contributing to 27% of attacks. It occurs when APIs allow access to endpoints managing object identifiers without verifying if the user has the right permissions. Attackers exploit this by modifying IDs in requests (e.g., changing /api/users/123/orders to /api/users/124/orders) to access unauthorized data.

The root cause is authorization failure. Authentication alone isn’t enough - permissions must be validated for every data access. To reduce this risk:

  • Verify ownership in every function that uses user-supplied IDs.
  • Use random, hard-to-guess identifiers like UUIDs instead of sequential numbers.
  • For complex permission models, consider Attribute-Based Access Control (ABAC), which evaluates permissions based on user, resource, and environment attributes.

Injection Attacks: SQL, NoSQL, and SSRF

Injection vulnerabilities allow attackers to inject malicious code into API requests, targeting databases and internal systems. While SQL and NoSQL injection remain threats, Server-Side Request Forgery (SSRF) has been added as a standalone category (API7:2023). SSRF happens when an API fetches a remote resource based on a user-supplied URI without proper validation, potentially exposing internal networks, cloud metadata services (like 169.254.169.254), or loopback addresses.

To mitigate these risks:

  • Use parameterized queries or ORM tools to prevent SQL and NoSQL injection. Avoid concatenating user inputs into queries.
  • Enforce strict input validation using schema validation libraries for all inputs (e.g., body, query parameters, headers).
  • For SSRF, restrict user-provided URIs to an allowlist, permit only specific protocols (like HTTPS), and implement network segmentation to block access to internal systems.

Security Misconfiguration

Security misconfiguration (API8:2023) accounts for 54% of attacks. These issues arise from improperly configured APIs or supporting systems, often due to complex settings, default configurations, or neglecting security best practices. Common examples include leaving default admin credentials unchanged, exposing detailed error messages in production, enabling unnecessary HTTP methods, or using wildcard origins (*) in CORS policies with credentials.

Misconfiguration Impact Mitigation
Verbose Errors Information Leakage Return generic error codes; log details internally
Insecure CORS Cross-Domain Data Theft Whitelist origins; avoid wildcards with credentials
Default Credentials Full System Compromise Replace default credentials with strong, unique keys
Missing HSTS Man-in-the-Middle (MitM) Enforce Strict-Transport-Security headers
Debug Endpoints Unauthorized Access Disable nonessential routes in production

To strengthen configurations:

  • Automate security checks in your CI/CD pipeline.
  • Use middleware (e.g., Helmet.js) to apply headers like X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and Content-Security-Policy: default-src 'none'.
  • Remove unnecessary endpoints, debug routes, and HTTP methods before deploying to production.
  • Ensure error handlers provide generic messages with unique request IDs for internal tracking, avoiding exposure of raw system errors.

Next, we’ll explore how modern authentication methods, such as OAuth 2.1, tackle similar security challenges.

OAuth 2.1 Authentication Guide

OAuth 2.1 brings together the features of OAuth 2.0 with years of security improvements, like PKCE, Token Revocation, and Native App Best Practices, into a single, streamlined standard. The aim? To make secure implementation the default by eliminating outdated grant types and enforcing features that were previously optional.

"OAuth 2.1 consolidates a decade of security learnings into a single, prescriptive specification. It removes deprecated grant types, mandates security features like PKCE, and provides clear guidance that makes secure implementation the default path." - Asad Ali, Founder & Principal Engineer

One of the most notable updates is the mandatory use of PKCE for all clients, including server-side applications, to block authorization code injection and interception attacks. OAuth 2.1 also discontinues the Implicit Grant and Resource Owner Password Credentials (ROPC) flows, as they expose tokens and user credentials in insecure ways. Additionally, authorization servers must now enforce exact string matching for redirect URIs, eliminating the risks associated with wildcards or prefix matching. Below, we’ll break down these updates, starting with PKCE, the removal of insecure flows, and the introduction of refresh token rotation.

Feature OAuth 2.0 OAuth 2.1
PKCE Optional (recommended for public clients) Mandatory for all clients
Implicit Grant Allowed Removed
Password Grant (ROPC) Allowed Removed
Redirect URI Matching Loose/Wildcards allowed Exact string matching required
Refresh Token Rotation Optional Required for public clients

PKCE: Why It's Required in OAuth 2.1

Proof Key for Code Exchange (PKCE) was initially designed to protect mobile and single-page applications from authorization code interception. Now, OAuth 2.1 mandates its use for all client types, including confidential server-side apps, to guard against authorization code injection.

Here’s how PKCE works: Before starting the OAuth flow, the client generates a secure random string called the code_verifier (43–128 characters long) and creates a code_challenge using SHA-256 (S256 method). The challenge is sent with the authorization request. Later, when the client exchanges the authorization code for tokens, it must provide the original verifier. The authorization server checks that the hash matches, ensuring the same client completes the process.

"The client secret protects the token endpoint; PKCE protects the authorization flow." - Pockit Tools

For maximum security, always use the S256 method for the code challenge. The "plain" method doesn’t offer sufficient protection. If you're working with single-page applications, store the code_verifier in sessionStorage instead of localStorage. This way, it’s cleared when the browser tab closes, reducing the risk of cross-site scripting (XSS) attacks.

Removed Flows: Implicit and Password Credentials

To enhance security, OAuth 2.1 eliminates flows that could compromise token safety. The Implicit Grant exposed access tokens in URL fragments, making them vulnerable. Similarly, the Resource Owner Password Credentials (ROPC) flow required apps to handle raw user passwords, increasing the chance of phishing attacks.

These outdated methods are replaced by the Authorization Code flow with PKCE, which provides a more secure alternative without relying on a client secret. This shift strengthens protection for all application types.

Refresh Token Rotation

OAuth 2.1 introduces refresh token rotation as a requirement for public clients like mobile and single-page applications. Each time a refresh token is used to obtain a new access token, the authorization server issues a fresh refresh token and invalidates the old one. This one-time-use approach limits potential damage if a token is stolen.

Authorization servers must track token families. If an invalidated token is reused, it signals a possible theft or replay attack, prompting the server to revoke the entire token family and require the user to re-authenticate.

To further secure refresh tokens, store them in cookies with the httpOnly, Secure, and SameSite (Lax or Strict) attributes. Access tokens should expire within 15–30 minutes, while refresh tokens should have a lifespan of no more than 7 days. For environments requiring higher security, consider using Demonstration of Proof-of-Possession (DPoP). This binds tokens to a specific client’s cryptographic key pair, ensuring stolen tokens can’t be reused.

JWT Security: Implementation and Common Mistakes

JSON Web Tokens (JWTs) are self-contained tokens used for authentication, consisting of three Base64URL-encoded parts separated by dots: header.payload.signature. The header includes metadata like the signing algorithm and token type, while the payload contains claims such as user ID, roles, or expiration. These claims are classified as Registered (standard claims like exp, iss, aud), Public, or Private (application-specific). Finally, the signature ensures the token hasn't been tampered with.

JWTs are widely adopted - 92% of web applications utilize OAuth 2.0 for tasks like social login or API authorization as of 2024. However, misconfigurations can lead to serious vulnerabilities.

JWT Structure and Verification

JWTs are encoded, not encrypted. This means anyone intercepting a token can decode its payload and view its contents. Security depends on the signature, which confirms the token's authenticity and integrity. For asymmetric signing, issuers often provide a /.well-known/jwks.json endpoint (JSON Web Key Set), allowing verifiers to dynamically fetch public keys for signature validation. This setup also supports key rotation without service interruptions.

Algorithm Type Key Requirement Best Use Case
HS256 Symmetric Shared secret (256-bit) Single-service apps where issuer and verifier are the same
RS256 Asymmetric RSA Private/Public key pair Distributed microservices and OAuth 2.0/OIDC
ES256 Asymmetric Elliptic Curve key pair Performance-sensitive systems and mobile apps

Secure JWT Implementation

To minimize risks, follow these practices:

  • Short-lived tokens: Set access token lifetimes to 5–15 minutes and use refresh tokens (lasting 7–30 days) for session continuity.
  • Secure storage: Store JWTs in cookies with httpOnly, Secure, and SameSite=Strict attributes. Avoid localStorage, as it is vulnerable to Cross-Site Scripting (XSS) attacks.
  • Claim validation: Always validate critical claims like exp (expiration), iss (issuer), and aud (audience) to ensure the token is still valid and intended for your service.
  • Payload safety: Avoid including sensitive data like passwords or personally identifiable information (PII) in the payload, as JWTs are only Base64URL-encoded.

For symmetric algorithms like HS256, use a cryptographically secure random secret of at least 256 bits (32 bytes). Avoid weak secrets like "password" or "secret" that could be brute-forced. In distributed systems, prefer RS256 or ES256 over HS256, as asymmetric algorithms allow multiple services to verify tokens using a public key, reducing the risk of token forgery.

Common JWT Vulnerabilities and Fixes

JWT implementations are prone to several vulnerabilities if not configured carefully. Here are the most critical issues and how to address them:

  1. The none algorithm exploit
    The JWT specification allows an alg: none header, which disables signature verification. Attackers can exploit this to modify the payload and bypass authentication entirely.
    Fix: Explicitly whitelist allowed algorithms during verification (e.g., jwt.verify(token, key, { algorithms: ['RS256'] })).
  2. Algorithm confusion attacks
    Attackers can switch the algorithm from RS256 (asymmetric) to HS256 (symmetric) and use the server's public key as the HMAC secret to forge tokens.
    Fix: Do not rely on the alg header in the token. Instead, enforce algorithm validation in your verification logic.
  3. Lack of token revocation
    Without a revocation mechanism, compromised tokens remain valid until they expire.
    Fix: Use a jti (JWT ID) claim and maintain a blocklist (e.g., in Redis) to revoke specific tokens as needed.
Claim Name Validation Requirement Risk if Skipped
exp Expiration Must be in the future Tokens remain valid indefinitely
iss Issuer Must match your auth server Accepting tokens from untrusted sources
aud Audience Must match your service ID Tokens for one service might work on another
jti JWT ID Check against a blocklist Cannot revoke specific tokens

Authorization Patterns: RBAC, ABAC, and Scopes

Authentication answers the question of who you are, while authorization determines what you’re allowed to do. Once your API confirms a user's identity using OAuth 2.1 or JWT, the next step is enforcing access control. This is essential for protecting against vulnerabilities like BOLA (Broken Object Level Authorization), which remains the most common API vulnerability. Gartner even forecasts that by 2026, APIs will become the leading attack vector for enterprise web applications.

To build a robust API security framework, pairing OAuth 2.1 and JWT with effective authorization models is key. The three main approaches are Role-Based Access Control (RBAC), Attribute-Based Access Control (ABAC), and Scope-Based Access Control. Many advanced SaaS platforms combine these methods, often using RBAC for broad feature access and ABAC for more precise, record-level permissions.

Role-Based Access Control (RBAC)

RBAC works by assigning permissions to roles (like admin, editor, or viewer) instead of individual users. When a user logs in, the API checks their role and grants the appropriate access. This method is ideal for organizations with well-defined job roles and stable hierarchies.

The simplicity of RBAC is its biggest advantage. It’s easy to manage and audit since access is tied to roles, making it a great choice for early-stage applications. However, RBAC can become unwieldy when faced with complex requirements. For example, scenarios like "editors can only modify their own documents" or "managers can approve requests only during business hours" often lead to role explosion - the creation of numerous overly specific roles.

"A well-implemented RBAC system will always be more secure than a poorly implemented ABAC system." – Lorikeet Security Team

For startups or smaller applications, RBAC is a great starting point. But if you find yourself managing 20–30 roles or more, it might be time to explore more advanced models. Always enforce permissions at the API layer rather than relying on UI restrictions alone.

Attribute-Based Access Control (ABAC)

ABAC bases its decisions on a combination of attributes, such as user details (e.g., department, clearance level), resource properties (e.g., document ownership), actions (e.g., read, write, delete), and environmental factors (e.g., time of day, location).

This model allows for context-aware access control. For instance, a financial API might permit transactions only during business hours, from trusted IP addresses, and for amounts within a user's approval limit. ABAC is particularly useful in industries like finance and healthcare, where access decisions depend on multiple, dynamic factors.

While ABAC provides fine-grained control, it comes with added complexity. Debugging access issues can be challenging when policies rely on numerous attributes, and missing or unreliable data can disrupt logic. ABAC may also introduce latency compared to simpler role lookups. To handle these challenges, many organizations use policy engines like Open Policy Agent (OPA), AWS Cedar, or Casbin to separate authorization logic from application code.

Scope-Based Access Control

OAuth scopes are different from RBAC and ABAC because they focus on restricting third-party access. Scopes define the specific actions or resources a third-party app can access on behalf of a user. For example, when a user authorizes an app to "read your profile" or "write to your calendar", they grant scopes like read:profile or write:calendar. However, scopes control app permissions, not user-specific rights. Even if an OAuth client is granted a scope like repo:read, the API must still verify which repositories the user can access.

Scopes are most effective for third-party integrations and public APIs, where you need to delegate limited access to external applications. For internal systems, RBAC or ABAC is often a better fit. Always enforce scopes at the route level by ensuring endpoints check for the required scope in the token.

To further protect against BOLA attacks, ensure that endpoints validate resource ownership using UUIDs and explicit ownership filters. These layered approaches to authorization provide a solid foundation for securing APIs, paving the way for discussions on gateway security and testing in the next sections.

API Gateway Security: WAF, Request Signing, and mTLS

An API gateway serves as the central checkpoint for securing traffic between clients and backend services. Acting as a reverse proxy, it handles tasks like authentication, rate limiting, and request transformation before the requests ever reach the application logic [43, 44]. This centralized setup ensures consistent implementation of security policies across all APIs, eliminating the need to implement these measures individually in every microservice.

One of the gateway's most critical security features is its ability to "fail closed." This means that when security checks fail, the gateway will reject requests by default, preventing unauthorized access even during times of system stress or configuration issues. With the increasing frequency of API-related breaches costing millions, the gateway becomes your frontline defense. This role naturally extends into more advanced techniques like integrating WAF and mTLS.

Web Application Firewalls (WAF) and Request Signing

A Web Application Firewall (WAF) is designed to detect and block malicious requests, such as those containing SQL injection, cross-site scripting (XSS), or command injection attempts [43, 3]. Most modern API gateways can integrate with WAF solutions, whether you're using managed services like AWS WAF and Azure WAF or hosting your own with tools like ModSecurity. A good starting point is the OWASP ModSecurity Core Rule Set (CRS), which you can customize with API-specific rules - for example, scanning JSON bodies for SQL keywords or capping JSON nesting depth at 10 levels to prevent resource exhaustion.

To further tighten security, configure your WAF to:

  • Block requests exceeding size limits (e.g., 10MB).
  • Enforce strict timeouts, such as 10 seconds for connections and 30 seconds for reads.
  • Strip internal headers like X-Powered-By to prevent information leaks.

Additionally, set up alerts for spikes in specific HTTP status codes like 401 (Unauthorized), 403 (Forbidden), and 429 (Too Many Requests). These spikes can signal credential stuffing or scraping attempts [43, 3].

Request signing with HMAC (Hash-based Message Authentication Code) adds another layer of security by ensuring the integrity and authenticity of API requests. Here's how it works: the client generates an HMAC signature using a shared secret and includes it in the request header. The server then recalculates the signature and rejects any mismatches, guaranteeing that the request hasn't been tampered with. This technique is especially useful for scenarios like webhooks, license verification, and server-to-server communication.

Method Security Level Best Use Case
API Keys Low Simple server-to-server identification
JWT Bearer Medium-High User-facing APIs and microservices
OAuth2 + OIDC High Third-party access and Single Sign-On (SSO)
mTLS Very High Internal service-to-service (Zero Trust)

While payload inspection and signing protect the data, client authentication through mutual TLS takes security to the next level.

Mutual TLS (mTLS) for Client Authentication

Mutual TLS (mTLS) goes beyond standard TLS. Instead of just the server presenting a certificate, both the client and the server exchange X.509 certificates during the TLS handshake. This creates a two-way verification process, ensuring that only authenticated clients and servers can communicate. Even if a service is compromised, it cannot impersonate another without its private key. This makes mTLS a cornerstone of zero-trust architectures and microservices security best practices.

Service meshes like Istio and Linkerd simplify mTLS adoption by using sidecar proxies, such as Envoy, to handle certificate exchanges automatically. This means your application code remains untouched. In production environments, it's recommended to enable STRICT mode to block any non-mTLS connections. Automating certificate management is also critical - tools like cert-manager in Kubernetes can handle issuance, storage, and renewal of certificates every 90 days without manual intervention.

For optimal security, mTLS can be paired with OAuth 2.1 and robust JWT handling. While mTLS excels in machine-to-machine (M2M) communication, it can be operationally complex and isn't practical for user-facing APIs accessed through browsers. Managing client certificates on end-user devices introduces significant challenges. As a result, many organizations use mTLS for transport-layer identity and JWT for application-layer authorization, combining these approaches to achieve a layered defense strategy.

Security Testing Tools: SAST, DAST, and API Scanners

With the rise in API-related attacks, manual code reviews alone are no longer enough. Tools like SAST, DAST, and API scanners address different vulnerabilities at various stages of development, making them indispensable for a strong security process.

Static Application Security Testing (SAST) examines your source code, binaries, or bytecode without running the application. This approach helps identify issues such as hardcoded API keys, insecure libraries, and SQL injection patterns early in the development process. By integrating SAST into your CI/CD pipeline, you can automatically scan every pull request. Configure it to fail builds if it detects "High" severity vulnerabilities or secrets in your repository. Additionally, tools like npm audit can complement SAST by scanning for vulnerable third-party dependencies.

Dynamic Application Security Testing (DAST), on the other hand, evaluates live APIs by simulating real-world attacks. This method uncovers vulnerabilities that only appear during runtime, such as security misconfigurations, SSRF, and authentication flaws. While SAST focuses on white-box testing, DAST operates as black-box testing.

Static Application Security Testing (SAST)

SAST tools scan your codebase during the build phase, helping you catch vulnerabilities before deployment. They're particularly effective at spotting hardcoded secrets, weak cryptographic implementations, and coding patterns prone to injection attacks. Since SAST works directly with source code, it pinpoints the exact file and line number of a vulnerability, making remediation straightforward.

However, one common drawback is false positives. Without runtime context, SAST may flag non-vulnerable code, which is why it works best as an initial layer of defense. For runtime-specific flaws, dynamic testing is essential.

Dynamic Application Security Testing (DAST)

DAST tools simulate attacks on your running API to uncover vulnerabilities caused by component interactions. They test for issues like broken access control, a problem affecting 91% of web applications as of 2025. By sending malicious payloads and manipulating parameters, DAST tools mimic actual attackers, making them effective at finding runtime issues.

For example, OWASP ZAP is a free, open-source DAST scanner with API-specific capabilities. Importing your OpenAPI or Swagger specification into ZAP ensures all endpoints are tested, including those not linked in the UI. You can even integrate ZAP into GitHub Actions using zap-api-scan.py to automate scans during the build process.

For more advanced testing, Burp Suite offers Out-of-band Application Security Testing (OAST) through its Burp Collaborator feature. This can detect "invisible" vulnerabilities like asynchronous SQL injection and blind SSRF, which traditional DAST tools might overlook. As James Kettle from PortSwigger notes:

"Burp Scanner is capable of finding many critical vulnerabilities that even an expert manual tester could easily miss - like deferred asynchronous command injection."

Combining automated scans with manual API testing tools further strengthens your security efforts.

API Testing Tools: Burp Suite, OWASP ZAP, and Postman

Burp Suite

Each tool plays a specific role in securing your APIs. Burp Suite, a leading tool for manual API testing, lets you intercept and modify requests, test for Broken Object Level Authorization (BOLA), and ensure users can't access unauthorized objects. While its Professional version offers advanced features, the free Community Edition provides basic interception capabilities.

Postman, often known for functional testing, can also be used for security testing. You can create structured test collections to cover the OWASP API Security Top 10. Automated scripts can verify controls like rate limiting, input validation, and proper error handling after each API call.

For more specialized testing, tools like jwt_tool can assess JWT weaknesses, such as algorithm confusion or weak secrets, while sqlmap is ideal for deep SQL injection testing. These targeted tools complement general scanners by focusing on specific attack vectors.

A multi-layered approach combining these tools ensures comprehensive API security.

Tool Type Primary Focus Best Integration Point
SAST Source code, secrets, and configuration Pull Request / Commit stage
DAST Runtime behavior, auth flaws, and BOLA Post-deployment to staging/test env
API Scanners REST/GraphQL endpoints, BOLA, Mass Assignment Testing / Staging / Production

To maximize protection, run SAST scans at every commit, execute DAST scans in staging environments, and use manual tools like Burp Suite for complex tests. These foundational practices can block approximately 80% of common API attacks, creating a strong defense as part of a well-rounded security lifecycle.

Production-Ready API Security Checklist

To ensure your APIs are secure and ready for production, it’s essential to address every critical measure. With API abuse projected to account for 38% of data breaches by 2025, even a single oversight could result in losses averaging $4.88 million.

"API security is not a feature you add at the end - it is a design principle that shapes every decision from schema design to error handling."

Authentication and Authorization

Secure authentication and authorization are non-negotiable. Follow OAuth 2.1 and JWT best practices by validating every request. Use OAuth 2.1 with PKCE for all client types, including mobile and single-page apps. Avoid outdated flows like Implicit and Password Credentials. Access tokens should expire within 5–15 minutes and be signed with RS256 or ES256. Always validate token claims (iss, aud, exp) for every request.

For refresh tokens, implement rotation with single-use tokens stored in cookies using attributes like HttpOnly, Secure, and SameSite=Strict. If you’re using API keys, hash them with SHA-256 before storing them, and add environment-specific prefixes (e.g., sk_live_) to reduce accidental misuse. Use constant-time comparison methods (like crypto.timingSafeEqual) to thwart timing attacks when comparing keys or tokens.

To combat Broken Object Level Authorization (BOLA), verify resource ownership for every request instead of just relying on token validity. Replace sequential integer IDs with UUIDs (preferably version 7) to prevent enumeration attacks, and respond with 404 Not Found instead of 403 Forbidden to avoid revealing sensitive resource existence.

Input Validation and Rate Limiting

Strict input validation is a must. Use libraries like Zod (TypeScript), Pydantic (Python), or Joi (Node.js) to define valid input schemas (allowlisting). Avoid trying to filter out known malicious patterns. Also, set request size limits at the application or reverse proxy level to avoid resource abuse.

Implement tiered rate limits based on endpoint sensitivity. For example, authentication routes should allow no more than 5 requests every 15 minutes, while public read endpoints can handle 100 requests per minute. Use a distributed store like Redis to enforce these limits across multiple API instances, and communicate limits to clients using headers like X-RateLimit-Limit, X-RateLimit-Remaining, and Retry-After.

Endpoint Type Recommended Limit Window Rationale
Login/Auth 5 requests 15 minutes Brute-force protection
Password Reset 3 requests 1 hour Abuse prevention
Public Read 100 requests 1 minute Prevent scraping
Write Operations 30 requests 1 minute Prevent spam/resource exhaustion

To prevent injection attacks, always use parameterized queries for SQL databases and sanitize inputs for NoSQL databases. For GraphQL APIs, add query depth limits and cost-based complexity analysis to block resource-intensive nested queries.

Monitoring and Logging

Monitoring and logging are essential to detect and respond to threats. Log all security-relevant events, such as authentication attempts (both successful and failed), authorization denials, rate limit breaches, and input validation errors. Use structured JSON logging for easier integration with analysis tools and assign unique Request IDs (e.g., UUIDs) to every API call for seamless tracing.

When logging, avoid sensitive details like full API keys, passwords, or session tokens. Instead, log a fingerprint or a masked version (e.g., the first 8 characters of an API key). Maintain detailed audit trails for administrative actions and permission changes.

Set up automated alerts for unusual patterns, such as spikes in 401/403 errors, repeated rate limit violations, rapid requests from a single IP, or geographically improbable login attempts (e.g., from two distant locations in a short time). Flag authentication attempts from unfamiliar geolocations and ensure that while clients receive generic error messages, detailed logs are securely stored on the server.

Finally, include critical security headers in every API response:

  • Strict-Transport-Security (max-age: 63072000 seconds)
  • X-Content-Type-Options: nosniff
  • X-Frame-Options: DENY
  • A strict Content-Security-Policy

Conclusion

API security isn't something you tack on at the last minute - it’s a core principle that should guide every decision, from designing your first endpoint to deploying in production. By 2026, APIs are expected to handle over 80% of web traffic, and Gartner warns they’ll likely become the top attack vector for enterprise applications by then. Ignoring security until the end simply isn’t an option anymore.

Here’s the silver lining: most API vulnerabilities aren’t the result of complex, hard-to-detect exploits. Instead, they often stem from preventable issues like broken authentication, missing checks on resource ownership, or poor input validation. Steps like adopting OAuth 2.1 with PKCE, using proper JWT handling with asymmetric signing, validating resource ownership on every request, and enforcing rate limits can address many of these weaknesses before they escalate into breaches.

"Secure by default. Verify everything. Trust nothing." – ZTABS Team

Security isn’t just about compliance; it’s an ongoing discipline that requires constant attention. Use the OWASP API Security Top 10 as a guide for audits, automate security testing in your CI/CD pipeline, and ensure you have detailed logging for authentication and authorization events. With API attack traffic projected to rise by 680% between 2021 and 2025, and the average breach costing $4.88 million, investing in strong security practices is not just smart - it’s essential.

Take the production checklist outlined here, weave these practices into your workflow, and remember: every endpoint you secure today is one less vulnerability tomorrow. APIs are the backbone of your application - protect them like it.

FAQs

Do I still need OAuth 2.1 if I already use JWTs?

Yes, OAuth 2.1 is still essential even if you're using JWTs. Here's why: OAuth 2.1 isn't just about the type of token you use - it addresses the entire process of delegation, consent, and token issuance. It builds on OAuth 2.0 by introducing key security improvements, such as mandatory PKCE (Proof Key for Code Exchange) and simplified flows, which help close potential security gaps.

While JWTs (JSON Web Tokens) are a format for stateless token validation, they don't manage the broader framework needed for secure authorization. OAuth 2.1 ensures your system adheres to modern security protocols and best practices, keeping your application safe and up-to-date.

How can I stop BOLA without breaking my API design?

To guard against Broken Object Level Authorization (BOLA) while keeping your API design intact, prioritize server-side access controls. Always verify user permissions for each object request directly on the server - never depend on client-side checks.

Adopt granular access control mechanisms like Role-Based Access Control (RBAC) or Attribute-Based Access Control (ABAC) to manage permissions effectively. Cross-check user permissions against resource ownership to ensure that only authorized users can access specific data. And remember, client-supplied data cannot be trusted - validation must happen server-side.

By focusing on explicit server-side validation, you can build APIs that are both secure and scalable.

What’s the safest way to store access and refresh tokens?

The best way to store access and refresh tokens is by using HTTP-only, secure cookies. These cookies cannot be accessed through JavaScript, which helps protect against XSS attacks. Steer clear of storing tokens in localStorage or sessionStorage, as they are more susceptible to such vulnerabilities.

For added security, implement refresh token rotation and rely on short-lived access tokens. This approach minimizes the risk and limits the potential damage if a token is ever compromised, ensuring tighter control over token management.

Related Blog Posts

Why not level up your reading with

Stay up-to-date with the latest developer news every time you open a new tab.

Read more