Cybersecurity20 min read

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.

I
Microsoft Cloud Solution Architect
OWASPAPI SecurityREST APIBroken Object Level AuthorizationJWTBOLAAPI vulnerabilitiesWeb Security

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 — 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 invoice
Vulnerable 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:
PatternProblem
No rate limit on /auth/loginCredential stuffing and brute force
JWT signed with HS256 + weak secretOffline brute-force of the signing key
JWT with alg: none acceptedSignature verification bypassed
Access token valid for 30 daysLong window for stolen token abuse
Credentials in URL query paramsLogged 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 restricted
Vulnerable 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:
MisconfigurationImpact
CORS: * (allow all origins)Any website can make credentialed requests to your API
Verbose errors in productionStack traces expose framework versions, file paths, SQL queries
HTTP methods not restrictedDELETE, PUT accepted on read-only resources
No security headersMissing X-Content-Type-Options, Strict-Transport-Security
Debug endpoints in production/debug, /actuator, /metrics expose internal state
Swagger UI publicly accessibleExposes 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:
  1. Attacker compromises a weather API your application uses
  2. The compromised API returns a malicious payload in a field your code injects into HTML
  3. 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 this

Also: 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 to automate API security testing (DAST, schema validation, auth testing) into your pipeline rather than relying on pre-release manual review.

I

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.

Share this article

Questions & Answers

Related Articles

Need Help with Your Security?

Our team of security experts can help you implement the strategies discussed in this article.

Contact Us