API Security
OWASP
API Security
BOLA
SSRF
+3 more

OWASP API Security Top 10 (2023): Every Vulnerability Explained With Real Attacks

SCR Security Research Team
February 5, 2026
22 min read
Share

What Is the OWASP API Security Top 10?

The OWASP API Security Project maintains a separate Top 10 list specifically for APIs, distinct from the web application OWASP Top 10. APIs have unique attack patterns that web-focused frameworks don't adequately address.

Why a Separate List? APIs expose application logic and sensitive data directly. Unlike web applications, APIs don't have a UI that limits what users can do — any client can send any request to any endpoint. This makes authorization, input validation, and rate limiting critical at the API layer.

The latest version (2023) was a complete overhaul from the 2019 edition:

#API Security Risk (2023)Change from 2019
API1Broken Object Level Authorization (BOLA)Same (#1 both years)
API2Broken AuthenticationUpdated
API3Broken Object Property Level AuthorizationNew (merged mass assignment + excessive data exposure)
API4Unrestricted Resource ConsumptionNew (replaces Lack of Resources)
API5Broken Function Level AuthorizationSame
API6Unrestricted Access to Sensitive Business FlowsNew
API7Server-Side Request Forgery (SSRF)New
API8Security MisconfigurationSame
API9Improper Inventory ManagementUpdated
API10Unsafe Consumption of APIsNew

API1: Broken Object Level Authorization (BOLA)

The #1 API vulnerability — also known as IDOR (Insecure Direct Object Reference) in web context. An attacker changes an object ID in a request to access another user's data.

Attack Scenario:

# Legitimate request
GET /api/v2/accounts/1001/transactions
Authorization: Bearer <user_1001_token>

# Attack — change account ID
GET /api/v2/accounts/1002/transactions
Authorization: Bearer <user_1001_token>
# Server returns user 1002's transactions — BOLA vulnerability!

Fix Pattern:

// Verify object ownership on EVERY request
async function getTransactions(req: Request) {
  const accountId = req.params.accountId;
  const userId = req.auth.userId;

  // Check ownership BEFORE returning data
  const account = await Account.findById(accountId);
  if (!account || account.ownerId !== userId) {
    return new Response("Not found", { status: 404 }); // 404, not 403
  }

  const transactions = await Transaction.find({ accountId });
  return Response.json(transactions);
}

Best Practice: Return 404 (Not Found) instead of 403 (Forbidden) for unauthorized object access. A 403 confirms the object exists, enabling enumeration.


API2: Broken Authentication

API authentication failures include weak passwords, missing MFA, exposed credentials in URLs, and improper JWT validation.

Common JWT Mistakes:

// VULNERABLE — Not verifying JWT signature
const decoded = jwt.decode(token); // decode without verify!
const userId = decoded.userId;

// VULNERABLE — Algorithm confusion (accepts "none")
const decoded = jwt.verify(token, publicKey); // No algorithm restriction

// SECURE — Strict verification
const decoded = jwt.verify(token, process.env.JWT_SECRET, {
  algorithms: ["HS256"],      // Explicitly allow only expected algorithm
  issuer: "securecodereviews.com",
  audience: "api.securecodereviews.com",
  maxAge: "15m",              // Reject tokens older than 15 minutes
});

API3: Broken Object Property Level Authorization

APIs that return more data than the client needs, or allow clients to modify properties they shouldn't.

Excessive Data Exposure:

// VULNERABLE — Returns entire user object (including sensitive fields)
app.get("/api/users/:id", auth, async (req, res) => {
  const user = await User.findById(req.params.id);
  res.json(user); // Includes: password hash, SSN, internal notes, role!
});

// SECURE — Explicit field selection
app.get("/api/users/:id", auth, async (req, res) => {
  const user = await User.findById(req.params.id)
    .select("name email avatar createdAt"); // Only public fields
  res.json(user);
});

Mass Assignment:

// VULNERABLE — Accepts any fields from request body
app.put("/api/users/:id", auth, async (req, res) => {
  await User.findByIdAndUpdate(req.params.id, req.body); // Attacker sends { role: "admin" }!
});

// SECURE — Allowlist updateable fields
app.put("/api/users/:id", auth, async (req, res) => {
  const allowedFields = ["name", "email", "avatar"];
  const updates = {};
  for (const field of allowedFields) {
    if (req.body[field] !== undefined) updates[field] = req.body[field];
  }
  await User.findByIdAndUpdate(req.params.id, updates);
});

API4: Unrestricted Resource Consumption

APIs without proper rate limiting and resource constraints are vulnerable to DoS and financial abuse.

// Comprehensive rate limiting
import rateLimit from "express-rate-limit";

// Global rate limit
app.use(rateLimit({
  windowMs: 15 * 60 * 1000,  // 15 minutes
  max: 1000,                   // 1000 requests per window
  standardHeaders: true,
}));

// Sensitive endpoint rate limit
app.use("/api/auth/login", rateLimit({
  windowMs: 15 * 60 * 1000,
  max: 5,                     // 5 login attempts per 15 minutes
  message: { error: "Too many login attempts. Try again later." },
}));

// Expensive operation rate limit
app.use("/api/reports/generate", rateLimit({
  windowMs: 60 * 60 * 1000,
  max: 10,                    // 10 report generations per hour
}));

API5: Broken Function Level Authorization

Regular users can access admin functions by simply calling admin endpoints.


API6: Unrestricted Access to Sensitive Business Flows

APIs that expose business logic without proper controls — e.g., automated ticket buying, coupon abuse, review manipulation.

Example Attack: An API for an e-commerce site allows adding items to cart and checking out. An attacker automates the entire flow to buy limited-edition items before legitimate customers.

Defenses: CAPTCHAs for sensitive flows, device fingerprinting, behavioral analysis, purchase velocity limits.


API7: Server-Side Request Forgery (SSRF)

APIs that fetch URLs provided by users can be tricked into accessing internal resources.

// VULNERABLE — Fetches any URL the user provides
app.post("/api/fetch-url", async (req, res) => {
  const response = await fetch(req.body.url); // Can access internal services!
  res.json(await response.json());
});

// Attacker sends: { "url": "http://169.254.169.254/latest/meta-data/iam/" }
// Gets AWS instance metadata and credentials!

API8: Security Misconfiguration

Missing CORS restrictions, verbose errors, unnecessary HTTP methods enabled, missing security headers.


API9: Improper Inventory Management

Organizations don't know all their APIs — shadow APIs, zombie APIs, and undocumented endpoints.


API10: Unsafe Consumption of APIs

When your API consumes third-party APIs without proper validation, you inherit their vulnerabilities.


API Security Testing Matrix

RiskManual TestingAutomated Tool
BOLASwap object IDs between usersBurp Autorize extension
Broken AuthTest JWT manipulation, expired tokensjwt_tool, OWASP ZAP
Property AuthSend unexpected fields in PUT/PATCHBurp Intruder
ResourceSend oversized payloads, rapid requestsArtillery, k6
Function AuthCall admin endpoints as regular userAuthMatrix (Burp)
Business LogicAutomate purchase/reservation flowsCustom scripts
SSRFSend internal URLs (169.254.x, localhost)SSRFmap

Further Reading

Advertisement