OWASP API Security Top 10 2023: Complete Developer Guide with Real Examples
APIs are the fastest-growing attack surface. The OWASP API Security Top 10 2023 defines the most critical risks. This guide breaks down each risk with real attack examples, vulnerable code patterns, and concrete fixes.

Video transcript
Your mobile app talks to ten different A P I s every single day. But here's the thing: most developers have never heard of B O L A attacks. That's Broken Object Level Authorization, and it's number one on the O W A S P A P I Security Top ten for twenty twenty-three. Why should you care right now? Because A P I s are the fastest-growing attack surface in the world. When B O L A goes wrong, attackers literally walk into your database and steal customer records without breaking a single firewall. It happens because your code trusts the user's request instead of verifying who they actually are. Imagine a library where you ask for book number five hundred. If the system never checks whether you're allowed to read that book, anyone can ask for any number and get any book. B O L A works exactly like that. Your A P I accepts a user I D from the request and serves back that user's data without confirming the request came from that actual user. Now here's the second critical risk: excessive data exposure. Your A P I endpoint returns everything it has. It's like ordering a sandwich and the restaurant handing you the entire kitchen. You asked for user profile data, but the A P I also returned password hashes, internal I D s, and payment tokens. Attackers harvest all of it. Third: rate limiting and throttling failures let attackers brute force your endpoints. Picture a bouncer who never says no. Attackers fire millions of requests in minutes, guessing credentials or triggering password resets at scale. Without proper rate limiting, your A P I becomes a machine gun in their hands. Start today: audit one A P I endpoint in your system right now. Check if it verifies user permissions before returning data. Read the complete guide at protego dot me.
Why APIs Are Now the #1 Attack Surface
Every significant application breach in the last three years has involved an API. Not a buffer overflow in a kernel module or a decade-old CVE, but an API endpoint that did exactly what it was told, just by someone who shouldn't have been asking.
APIs have become the primary interface between everything: mobile apps, third-party integrations, microservices, partner systems, internal tools. The average enterprise now exposes hundreds of APIs, many of them undocumented, unmonitored, and tested only for functionality rather than security.
OWASP recognized this shift and published the API Security Top 10 2023 - a complete rewrite of the 2019 edition that reflects how API attacks have evolved. Unlike the web application Top 10, which focuses on server-side vulnerabilities, the API Top 10 addresses issues unique to how APIs are designed, authenticated, and consumed.
This guide walks through all ten risks with the attack pattern, vulnerable code, and the fix.
API1:2023 - Broken Object Level Authorization (BOLA)
What it is: The most common and most impactful API vulnerability. An attacker accesses another user's data by manipulating an object identifier in the request.
Attack pattern:
GET /api/v1/invoices/1234 ← Attacker's invoice
GET /api/v1/invoices/1235 ← Different user's invoice (just increment the ID)
GET /api/v1/invoices/1236 ← Another user's invoiceVulnerable code:
// VULNERABLE: Only checks auth, not ownership
app.get('/api/v1/invoices/:id', authenticate, async (req, res) => {
const invoice = await Invoice.findById(req.params.id)
res.json(invoice) // Returns any invoice if you know the ID
})Fixed code:
// SECURE: Checks ownership before returning data
app.get('/api/v1/invoices/:id', authenticate, async (req, res) => {
const invoice = await Invoice.findOne({
_id: req.params.id,
userId: req.user.id // Must belong to the requesting user
})
if (!invoice) return res.status(404).json({ error: 'Not found' })
res.json(invoice)
})Use non-guessable UUIDs instead of sequential IDs to reduce enumeration risk, but never treat UUIDs as a security control. Authorization logic is the fix.
API2:2023 - Broken Authentication
What it is: Weak authentication mechanisms - missing rate limiting on login endpoints, weak JWT implementation, credentials in query strings, long-lived tokens without rotation.
Common vulnerabilities:
| Pattern | Problem |
|---|---|
| No rate limit on `/auth/login` | Credential stuffing and brute force |
| JWT signed with HS256 + weak secret | Offline brute-force of the signing key |
| JWT with `alg: none` accepted | Signature verification bypassed |
| Access token valid for 30 days | Long window for stolen token abuse |
| Credentials in URL query params | Logged in proxy/server logs, browser history |
Dangerous JWT vulnerability:
// VULNERABLE: Accepting 'none' algorithm
const decoded = jwt.verify(token, secret, { algorithms: ['HS256', 'none'] })
// SECURE: Explicitly restrict to expected algorithm
const decoded = jwt.verify(token, secret, { algorithms: ['HS256'] })Best practices: Short-lived access tokens (15 min) + refresh token rotation, PKCE for OAuth flows, rate limiting with exponential backoff on auth endpoints, MFA for sensitive operations.
API3:2023 - Broken Object Property Level Authorization
What it is: APIs expose more object properties than the caller should be able to see or modify: either over-fetching (exposing sensitive fields) or over-posting (accepting fields that shouldn't be writable).
Over-exposure attack:
// API returns full user object including internal fields
{
"id": "123",
"email": "user@example.com",
"name": "Alice",
"role": "admin", ← Should not be exposed to end users
"internalUserId": "USR_89",
"creditCardLast4": "4242", ← Sensitive field exposed unnecessarily
"passwordResetToken": "abc" ← Critical: token in response!
}Mass assignment attack (over-posting):
PATCH /api/v1/profile
{
"name": "Alice",
"email": "alice@example.com",
"role": "admin", ← Attacker adds this, API accepts it
"isVerified": true ← And this
}Fix: Use explicit allow-lists for response serialization and request deserialization. Never bind request body directly to database models.
// SECURE: Explicit allow-list for what can be updated
const ALLOWED_UPDATE_FIELDS = ['name', 'email', 'bio', 'avatar']
app.patch('/api/v1/profile', authenticate, async (req, res) => {
const updates = pick(req.body, ALLOWED_UPDATE_FIELDS) // lodash.pick or equivalent
await User.findByIdAndUpdate(req.user.id, updates)
res.json({ success: true })
})API4:2023 - Unrestricted Resource Consumption
What it is: No limits on request rate, payload size, query complexity, or execution time, enabling DoS, resource exhaustion, and financial attacks (scraping paid APIs).
Attack vectors:
- High-frequency requests draining rate-limited paid services you're proxying
- Large file upload payloads consuming memory/disk
- Deep GraphQL queries or nested joins causing CPU spikes
- Webhook abuse - attacker-controlled endpoints causing thousands of outbound calls
Mitigations:
// Rate limiting example (express-rate-limit)
const rateLimit = require('express-rate-limit')
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window per IP
message: { error: 'Too many requests' },
standardHeaders: true,
legacyHeaders: false,
})
app.use('/api/', apiLimiter)
// Payload size limits
app.use(express.json({ limit: '10kb' })) // Reject large JSON bodies
app.use(express.urlencoded({ limit: '10kb', extended: true }))Also: set explicit timeouts on all external calls, implement pagination with max page size, and set GraphQL query depth/complexity limits.
API5:2023 - Broken Function Level Authorization
What it is: Admin or privileged functions are accessible to non-admin users because the API doesn't check the caller's role, only their authentication.
Pattern:
DELETE /api/v1/admin/users/456 ← Works even as a regular user if no role check
POST /api/v1/internal/export ← "Internal" in the path doesn't mean restrictedVulnerable pattern:
// VULNERABLE: Only checks authentication
router.delete('/admin/users/:id', authenticate, deleteUser)
// SECURE: Checks authentication + role
router.delete('/admin/users/:id', authenticate, requireRole('admin'), deleteUser)Combine BFLA protection with:
- Separate admin API surface (different subdomain or port, IP-restricted)
- Role claims in JWT that are server-verified on every request
- Audit logging of all privileged function calls
API6:2023 - Unrestricted Access to Sensitive Business Flows
What it is: Legitimate endpoints exploited at scale to abuse business logic: account creation for spam, inventory reservation attacks, coupon/promo code abuse, ticket scalping bots.
Unlike other API risks, these endpoints work exactly as designed, just not at the scale or by the actor intended.
Examples:
- Bots creating thousands of accounts with fake email addresses
- Competitors scraping your entire product catalog and pricing
- Reserving all available inventory in an e-commerce system then abandoning
- Referral fraud - creating fake accounts to claim referral bonuses
Mitigations: CAPTCHA on high-value flows, device fingerprinting, behavioral anomaly detection (flag account creation bursts), phone/email verification before granting full access, business-logic rate limits (max 3 accounts per device/IP/phone number).
API7:2023 - Server-Side Request Forgery (SSRF)
What it is: User-controlled input causes the server to make HTTP requests to internal services, cloud metadata endpoints, or arbitrary external URLs.
Attack in cloud environments:
POST /api/v1/fetch-preview
{
"url": "http://169.254.169.254/latest/meta-data/iam/security-credentials/prod-role"
}The server fetches the AWS metadata endpoint and returns the IAM role credentials to the attacker, potentially giving full AWS account access.
Fix:
const { URL } = require('url')
const dns = require('dns').promises
async function isSafeUrl(inputUrl) {
const parsed = new URL(inputUrl)
// Block non-HTTP protocols
if (!['http:', 'https:'].includes(parsed.protocol)) return false
// Block private/loopback ranges
const addresses = await dns.resolve4(parsed.hostname)
return addresses.every(ip => !isPrivateIP(ip)) // implement isPrivateIP check
}Also: use an allowlist of permitted external domains instead of a blocklist, deploy in a network where the API server has no access to internal services it doesn't need.
API8:2023 - Security Misconfiguration
What it is: Default configurations, excessive permissions, missing security headers, verbose error messages, unnecessary HTTP methods, and permissive CORS.
Most common API misconfigurations:
| Misconfiguration | Impact |
|---|---|
| `CORS: *` (allow all origins) | Any website can make credentialed requests to your API |
| Verbose errors in production | Stack traces expose framework versions, file paths, SQL queries |
| HTTP methods not restricted | DELETE, PUT accepted on read-only resources |
| No security headers | Missing `X-Content-Type-Options`, `Strict-Transport-Security` |
| Debug endpoints in production | `/debug`, `/actuator`, `/metrics` expose internal state |
| Swagger UI publicly accessible | Exposes full API documentation including internal endpoints |
Secure defaults:
// CORS: explicit allowlist
app.use(cors({
origin: ['https://yourapp.com', 'https://admin.yourapp.com'],
credentials: true,
methods: ['GET', 'POST', 'PUT', 'DELETE'],
}))
// Generic error responses in production
app.use((err, req, res, next) => {
if (process.env.NODE_ENV === 'production') {
return res.status(500).json({ error: 'Internal server error' }) // No details
}
res.status(500).json({ error: err.message, stack: err.stack })
})API9:2023 - Improper Inventory Management
What it is: Shadow APIs, deprecated API versions still active, undocumented endpoints, and APIs exposed through third-party services that bypass your security controls.
Why this matters: You can't secure what you don't know exists. Shadow APIs often arise from:
- API versions never properly deprecated (v1 still active when v3 is current)
- Developer/test APIs promoted to production without security review
- Third-party integrations that create undocumented API exposure
- Microservices that expose internal APIs without going through the API gateway
Inventory controls:
- API gateway as the single entry point - no direct service-to-internet exposure
- API discovery scanning (Postman API Network, APIsec, Salt Security)
- OpenAPI/Swagger specs enforced as part of CI/CD (PR blocks if spec not updated)
- Regular nmap scans of your own perimeter to find unexpected open ports/endpoints
- Version sunset policy: v1 deprecated → 90 days notice → disabled
API10:2023 - Unsafe Consumption of APIs
What it is: Your application consumes third-party APIs and trusts them unconditionally (not validating responses, following all redirects, or executing received content), making your application an attack vector when the upstream API is compromised.
Attack scenario:
- Attacker compromises a weather API your application uses
- The compromised API returns a malicious payload in a field your code injects into HTML
- Your users get XSS despite your own code being clean
Mitigations:
// Validate and sanitize ALL data from external APIs
const externalData = await thirdPartyApi.getData()
// Validate schema
const validatedData = externalApiSchema.parse(externalData) // Zod, Joi, etc.
// Sanitize any string fields before rendering
const safeName = sanitizeHtml(validatedData.name, { allowedTags: [] })
// Never execute content from external APIs
// eval(externalData.script) ← Never do thisAlso: use a fixed timeout on all outbound API calls, pin TLS certificates for critical partners, monitor for unexpected response structure changes (could indicate API compromise).
API Security Testing Checklist
Before shipping any API endpoint, verify:
Authorization
- [ ] Every endpoint checks object ownership (BOLA), not just authentication
- [ ] Admin/privileged functions check caller's role
- [ ] Object IDs are UUIDs, not sequential integers (defense-in-depth)
Authentication
- [ ] JWT algorithm explicitly restricted (no alg: none)
- [ ] Token expiry ≤ 15 minutes for access tokens
- [ ] Rate limiting on all auth endpoints
Input/Output
- [ ] Response serialization uses explicit field allow-list
- [ ] Request body allow-list (no mass assignment)
- [ ] Payload size limits configured
Infrastructure
- [ ] CORS configured with explicit origin allowlist
- [ ] No verbose error messages in production
- [ ] Swagger/docs not publicly accessible (or restricted to internal network)
- [ ] HTTP methods restricted to what's used
Monitoring
- [ ] Authentication failures logged and alerted
- [ ] Unusual access patterns (BOLA enumeration) detectable in logs
- [ ] API version inventory maintained and documented
The OWASP API Security Top 10 is a framework, not a checklist. The goal isn't to check boxes; it's to build the mental model of how APIs fail. Once you understand that authorization logic must be applied per-object (not just per-endpoint), that JWT implementation bugs are a class of their own, and that your trust in third-party APIs is a liability, the secure patterns become intuitive rather than memorized.
For organizations building APIs at scale, pair this with the [DevSecOps CI/CD guide](/blog/devsecops-integrating-security-into-cicd-pipelines-2026) to automate API security testing (DAST, schema validation, auth testing) into your pipeline rather than relying on pre-release manual review.
---
References
- [OWASP API Security Top 10 (2023)](https://owasp.org/API-Security/editions/2023/en/0x00-header/) — The official OWASP API Security Top 10 list
- [OWASP API Security Project](https://owasp.org/www-project-api-security/) — Project homepage with testing guides and resources
- [Postman State of the API Report 2024](https://www.postman.com/state-of-api/) — API usage and security breach statistics
- [NIST SP 800-204: Security Strategies for Microservices](https://csrc.nist.gov/publications/detail/sp/800-204/final) — API security architecture and service mesh guidance
Get weekly security insights
Cloud security, zero trust, and identity guides — straight to your inbox.
Microsoft Cloud Solution Architect
Cloud Solution Architect with deep expertise in Microsoft Azure and a strong background in systems and IT infrastructure. Passionate about cloud technologies, security best practices, and helping organizations modernize their infrastructure.
Questions & Answers
Related Articles
Microsoft Sentinel vs Defender XDR: Which Does Your Security Team Actually Need?
14 min read
The Vercel Breach Explained: How a Game Download Led to a Supply Chain Attack on 580 Employees
14 min read
Ransomware Protection: The Complete Defense Guide for 2026
20 min read
Need Help with Your Security?
Our team of security experts can help you implement the strategies discussed in this article.
Contact Us