OWASP published its API Security Top 10 in 2023, and the number one entry — Broken Object Level Authorization, or BOLA — has not moved from the top spot since the project's first edition in 2019. That persistence is the whole story of API security: the underlying mistake, trusting a client-supplied ID without re-checking server-side that the requester actually owns the object, is trivial to describe and remarkably common to ship. The 2023 list also introduced three new categories that didn't exist in 2019 — Unrestricted Access to Sensitive Business Flows, Server-Side Request Forgery, and Unsafe Consumption of APIs — reflecting how much of today's attack surface is APIs calling other APIs, not just a browser calling a backend. A typical REST service now exposes dozens of endpoints, each a potential BOLA or mass-assignment vector, and most teams have no single checklist that maps authentication, rate limiting, and input validation against that full list. This post is that checklist: what to enforce, why each control maps to a specific OWASP category, and where static and dynamic testing can verify you actually did it.
What should authentication and authorization actually enforce?
Authentication should issue short-lived, scoped OAuth2/OIDC tokens rather than long-lived static API keys for any endpoint acting on behalf of a user — a leaked static key has no expiry and no scope boundary, while a stolen access token typically expires in minutes and can't be replayed against endpoints outside its granted scope. Authorization is the harder half: every object-level access — "get order 4471," "update invoice 892" — must re-verify server-side that the authenticated caller owns or is permitted to see that specific object ID, not just that they hold a valid token. Skipping that check is Broken Object Level Authorization, OWASP's #1 API risk in both the 2019 and 2023 editions. The sibling issue, Broken Function Level Authorization (#5), happens when an endpoint checks "is this user logged in" but not "is this user an admin" before executing an admin-only action like a bulk delete. Both require the same fix: authorization logic that runs on every request, keyed to the specific object and function, never inferred from the existence of a valid session alone.
How do you stop mass assignment and excessive data exposure?
You stop both by treating the API's output and input contracts as an explicit allow-list, not whatever your ORM happens to serialize. OWASP's 2023 edition merged the old "Excessive Data Exposure" and "Mass Assignment" categories into a single item — Broken Object Property Level Authorization (#3) — because they're two sides of the same bug: an API that doesn't control which object properties a client can read or write. On the read side, don't serialize an entire database row and let the frontend decide what to display; return only the fields the endpoint's contract defines. On the write side, reject any request body field that isn't explicitly expected — a PATCH /users/:id endpoint that blindly binds JSON to a user model will let an attacker set isAdmin: true or accountBalance alongside the fields the form actually exposed. Schema validation against an OpenAPI or JSON Schema definition, with unknown properties rejected rather than silently dropped, closes both directions at once.
What does resource-consumption protection require beyond a basic rate limiter?
It requires limits scoped to API key, IP, and individual endpoint, plus caps on payload size, pagination depth, and query complexity — not just a global requests-per-minute ceiling. OWASP renamed this category from "Lack of Resources & Rate Limiting" to "Unrestricted Resource Consumption" in the 2023 edition specifically because rate limiting alone doesn't cover it: a single request can still exhaust resources through an unbounded limit query parameter, a deeply nested GraphQL query, or a batch endpoint accepting thousands of IDs in one call. Set explicit maximums on page size, array length in request bodies, and recursion/nesting depth for any query language you expose, and enforce them server-side before the request reaches business logic. Pair endpoint-level limits with per-key and per-IP throttling so a single compromised credential can't be used to exhaust a shared resource pool that legitimate users depend on.
How do you validate input against injection, SSRF, and mass assignment at once?
You validate at the schema boundary before any business logic runs, using strict, allow-listed rules rather than blocklists for what a field can contain. Every request body should be checked against a defined schema — types, formats, length bounds, and an explicit list of permitted fields — with unknown fields rejected outright, which is the same control that mitigates mass assignment under OWASP's #3 category. For any endpoint that accepts a URL and fetches it server-side (webhooks, image-proxying, PDF generation from a link), validate and canonicalize that URL against an allow-list of permitted schemes and hosts before the request goes out; this is the standard mitigation for Server-Side Request Forgery, OWASP's #7 category, which exists precisely because attacker-controlled URLs can be used to reach internal metadata endpoints or private network ranges a public API was never meant to expose. Content-type allow-listing and consistent output-encoding round out the same validation layer against injection.
Why do misconfiguration and inventory gaps matter as much as code-level bugs?
Because Security Misconfiguration (OWASP #8) and Improper Inventory Management (#9) are how a perfectly-coded endpoint still gets breached — through the copy of it nobody remembers exists. Misconfiguration covers the operational basics: TLS enforced everywhere, verbose stack traces and debug endpoints disabled in production, default credentials rotated, and security headers (CORS policy, Content-Security-Policy, Strict-Transport-Security) actually set rather than left at framework defaults. Inventory management is the newer, less-tested half: OWASP replaced "Improper Assets Management" with this category in 2023 to emphasize that most breaches hit forgotten API versions — the v1 endpoint left running after v2 shipped, or a staging deployment with production data and no monitoring. Maintaining a current OpenAPI specification for every environment, including deprecated versions still reachable in production, is what turns "we didn't know that endpoint existed" from a plausible excuse into a process failure.
How Safeguard helps
Several items on this checklist are exactly what Safeguard's application security testing is built to verify rather than just document. Safeguard's SAST traces untrusted input from source to sink across your codebase, so a mass-assignment-prone model binding or an unsanitized URL feeding a server-side fetch — the code-level root cause behind #3 and #7 — shows up as a dataflow trace with a CWE and OWASP mapping before it ever reaches production. Safeguard's DAST then tests the running API itself: missing security headers, injection classes, and misconfigurations under #8 get flagged through safe, non-destructive requests against verified, in-scope targets, with SAST and DAST findings correlated in one unified model so a confirmed, reachable runtime issue is prioritized ahead of a theoretical one. Neither engine replaces the authorization logic and rate-limit policy your team has to design — but both can confirm, continuously, that the checklist items you shipped are the ones actually running in production.