👋Hi, I'm Waqas — a Software Architect and Technical Consultant specializing in .NET, Azure, microservices, and API-first system design..
I help companies build reliable, maintainable, and high-performance backend platforms that scale.
OWASP API Security Top 10: What .NET Developers Must Know
OWASP API Top 10: broken auth, object-level auth, and mitigations in .NET.
July 2, 2025 · Waqas Ahmad
Read the article
Introduction
This guidance is relevant when the topic of this article applies to your system or design choices; it breaks down when constraints or context differ. I’ve applied it in real projects and refined the takeaways over time (as of 2026).
The OWASP API Security Top 10 lists the most critical risks for REST, GraphQL, and other APIs—each is a real-world vulnerability that attackers exploit, so treat it as your security baseline. This article explains what each of the ten risks is, why it matters, and how to fix it in .NET and Azure with concrete code (BOLA, broken auth, DTOs, rate limiting, SSRF, security headers, and more). For architects and tech leads, enforcing authorization per resource and per function, validating input/output, and hardening configuration reduces exposure before threat modelling and pen tests.
For a deeper overview of this topic, explore the full API Architecture guide.
System scale: Any API (internal or public) that handles user data, business operations, or sensitive flows. Applies when you’re designing or hardening API security and want a structured checklist.
Team size: Backend and security (or platform); someone must own auth, authorization, input validation, and rate limiting. Works when the team can map OWASP risks to real endpoints and fix them.
Time / budget pressure: Fits when you’re building or refactoring APIs and can prioritise BOLA, auth, and config; breaks down when there’s no ownership for security—then at least fix BOLA and auth first.
Technical constraints: .NET (ASP.NET Core); JWT, OAuth2; DTOs, rate limiting, security headers. Assumes you can enforce authorization per resource and per function.
Non-goals: This article does not replace a full security programme; it focuses on the OWASP API Top 10 and how to address each in .NET.
What is API security?
API security is the set of practices and controls that protect your API from unauthorized access, data theft, abuse, and misuse. Unlike traditional web apps (HTML forms and cookies), APIs are consumed by programs: mobile apps, SPAs, other services. That means authentication (who is calling?), authorization (what are they allowed to do?), input validation, rate limiting, and secure configuration all matter. The OWASP API Top 10 focuses on authorization and authentication failures, data exposure, misconfiguration, and inventory—the issues we see most often in production.
1. Broken Object Level Authorization (BOLA)
What it is. Broken Object Level Authorization (BOLA), also called IDOR (Insecure Direct Object Reference), means the API does not check that the caller is allowed to access the specific resource they requested. For example, a user changes the order ID in the URL from /orders/123 to /orders/456 and sees another user’s order because the API only checked “is the user logged in?” and not “does this order belong to this user?”
Why it matters. BOLA is the most common API vulnerability. Attackers enumerate IDs (numbers, GUIDs) or guess predictable identifiers and access or modify other users’ data. Every endpoint that takes an object identifier (order ID, profile ID, document ID) must verify that the current user (or client) is allowed to access that object.
Mitigation in .NET. After loading the resource, check ownership or permission before returning it. Use the authenticated user’s ID (from JWT or session) and compare with the resource’s owner or allowed list. Return 404 if not found and 403 Forbid if found but not allowed (to avoid leaking existence).
// OrdersController.cs – authorise per resource (BOLA mitigation)
[HttpGet("{id}")]
publicasync Task<IActionResult> GetOrder(Guid id)
{
var order = await _orderRepo.GetByIdAsync(id);
if (order == null) return NotFound();
if (order.UserId != _currentUser.Id) return Forbid();
return Ok(_mapper.Map<OrderDto>(order));
}
What it is. Broken Authentication means the API accepts weak or missing authentication: default credentials, API keys in URLs, missing or weak JWT validation, or session fixation. Attackers then impersonate users or gain access without valid credentials.
Why it matters. Once authentication is broken, authorization is meaningless. Attackers can act as any user, access tokens, or bypass login entirely.
Mitigation in .NET. Use OAuth2 / OpenID Connect for user sign-in; validate JWT (signature, issuer, audience, expiry). Never put API keys or secrets in URLs or client-side code. Use HTTPS only; store secrets in Azure Key Vault or environment variables. For machine-to-machine, use client credentials or Managed Identity; never share a single key across many clients.
What it is. This covers excessive data exposure (returning more fields than the client needs, including sensitive ones) and mass assignment (letting the client send any property and overwrite server state, e.g. isAdmin or balance). Both come from not defining a clear contract: what the API returns and what it accepts.
Why it matters. Over-exposure leaks internal fields, PII, or IDs. Mass assignment lets attackers elevate privileges or corrupt data by sending extra JSON properties.
Mitigation in .NET. Use DTOs and projection for responses: return only fields the client needs. For input, bind only allowed fields (input DTOs with explicit properties); ignore unknown properties; never bind directly to domain entities from the request body. Use [JsonIgnore] or allowlists.
// Response: DTO with only allowed fields (no internal IDs or PII)publicrecordOrderDto(Guid Id, string Status, decimal Total);
// Request: explicit input DTO – no IsAdmin, no BalancepublicrecordCreateOrderRequest(string ProductId, int Quantity);
// Controller – bind to DTO only; map to domain inside service
[HttpPost]
publicasync Task<IActionResult> Create([FromBody] CreateOrderRequest request)
{
var order = await _orderService.CreateAsync(_currentUser.Id, request);
return CreatedAtAction(nameof(GetOrder), new { id = order.Id }, order);
}
4. Unrestricted Resource Consumption
What it is. The API does not limit how much clients can ask for: no rate limiting, no pagination caps, no cost limits. Attackers (or buggy clients) can send huge requests, enumerate millions of IDs, or trigger expensive operations and exhaust CPU, memory, or database.
Why it matters. Unrestricted consumption leads to DoS, bill shock (e.g. expensive downstream calls), and degraded service for everyone.
Mitigation in .NET. Add rate limiting (per IP, per user, per key) and return 429 Too Many Requests with Retry-After. Cap page size and max top in list APIs. Use Azure API Management or middleware (e.g. AspNetCoreRateLimit) for rate limits. For expensive operations, use queues and quotas.
What it is. The API checks “is the user logged in?” but not “is this user allowed to call this function?” For example, a normal user calls an admin-only endpoint because the API only enforced authentication, not role or scope.
Why it matters. Attackers or curious users call privileged endpoints (delete user, export all data, change config) and escalate privileges.
Mitigation in .NET. Enforce authorization per endpoint: use [Authorize(Roles = "Admin")], policy-based authorization, or resource-based checks. Validate scopes for OAuth2 (e.g. api.write). Never rely only on hiding URLs; always check on the server.
6. Unrestricted Access to Sensitive Business Flows
What it is. A sensitive flow (e.g. “place order”, “transfer money”, “send password reset”) can be triggered without limit: no CAPTCHA, no step-up auth, no anti-automation. Bots or attackers automate abuse (bulk orders, credential stuffing, scraping).
Why it matters. Business logic abuse leads to fraud, spam, and unfair use. Sensitive flows need higher assurance and throttling.
Mitigation in .NET. Add rate limits and CAPTCHA (or similar) for sensitive flows; require step-up authentication (e.g. re-enter password) for high-value actions; log and alert on unusual patterns. Use Azure Bot Protection or custom heuristics.
What it is. The API takes a URL or host from the client and issues a server-side request to it. Attackers pass internal URLs (http://169.254.169.254/, http://localhost/admin) or open ports and read internal services or metadata.
Why it matters. SSRF can expose cloud metadata (e.g. IAM keys), internal APIs, or file systems. Never trust client-controlled URLs for outbound requests without strict allowlisting.
Mitigation in .NET.Allowlist allowed hosts or schemes; reject everything else. Do not use client input directly in HttpClient or WebRequest. Use Azure Managed Identity for internal calls so no secrets are in URLs. Validate and parse URLs; block private IP ranges and metadata endpoints.
// Validate URL before outbound request – allowlist scheme and hostvar uri = new Uri(clientProvidedUrl);
if (uri.Scheme != "https" || !AllowedHosts.Contains(uri.Host))
return BadRequest("URL not allowed");
8. Security Misconfiguration
What it is. Default credentials, verbose errors (stack traces to client), missing security headers (HSTS, CSP, X-Content-Type-Options), debug mode in production, or permissive CORS. Small misconfigurations add up to big risk.
Why it matters. Attackers use default passwords, error messages that reveal paths or versions, and missing headers to probe and exploit.
Mitigation in .NET. Use secure defaults: turn off detailed errors in production; use custom error middleware. Add security headers (HSTS, X-Content-Type-Options, etc.). Restrict CORS to known origins. Store secrets in Key Vault; never in config files in source. Use Azure App Service or AKS hardening checklists.
What it is. No clear list of API endpoints, versions, or environments. Old or deprecated APIs stay exposed; dev/staging endpoints use production data; documentation is missing or wrong. Attackers and developers alike hit the wrong thing.
Why it matters. You cannot secure what you do not know exists. Shadow APIs, old versions, and undocumented endpoints become the attack surface.
Mitigation in .NET.Document all APIs (OpenAPI/Swagger); version APIs (URL or header) and retire old versions with a plan. Use API Management or gateway to expose only intended endpoints. Separate environments and do not point dev at production data. Track inventory in ADRs or a registry.
10. Unsafe Consumption of APIs
What it is. Your API calls third-party or internal APIs without validating responses, checking TLS, or handling errors safely. You trust the other service too much: you parse XML/JSON blindly, follow redirects, or leak data in logs. Attackers abuse the third party or man-in-the-middle to inject bad data or steal tokens.
Why it matters. Your API is only as strong as the weakest link. Unsafe consumption leads to injection, data leakage, and downtime when the other API changes or is compromised.
Mitigation in .NET.Validate and sanitize all data from external APIs; use strong typing and schema validation. Use HTTPS and certificate validation; do not disable SSL checks. Limit what you send (no sensitive data in URLs or logs). Use timeouts and circuit breakers (e.g. Polly) so a bad upstream does not take you down.
// Validate response from external API – never trust blindlypublicasync Task<OrderDto?> GetExternalOrderAsync(string orderId)
{
var response = await _httpClient.GetAsync($"orders/{orderId}");
response.EnsureSuccessStatusCode();
var order = await response.Content.ReadFromJsonAsync<ExternalOrderDto>();
if (order == null || string.IsNullOrEmpty(order.Id))
thrownew InvalidOperationException("Invalid response from external API");
// Map to internal DTO – do not expose external structurereturnnew OrderDto(order.Id, order.Status, order.Total);
}
OWASP API Top 10 at a glance
#
Risk
What it is
Key mitigation
1
BOLA
API does not check if user owns the resource
Authorise per resource (check ownership)
2
Broken Authentication
Weak or missing auth (JWT, keys)
Validate JWT (issuer, audience, signature); use OAuth2
3
Broken Object Property Auth
Over-exposure or mass assignment
Use DTOs; bind only allowed fields
4
Unrestricted Resource Consumption
No rate limits or pagination caps
Rate limiting (429); cap page size
5
Broken Function Level Auth
User calls admin endpoints
[Authorize(Roles = "Admin")]; check scopes
6
Sensitive Business Flows
No CAPTCHA or step-up for sensitive ops
Rate limit + step-up auth + CAPTCHA
7
SSRF
Server requests client-controlled URL
Allowlist hosts; block private IPs
8
Security Misconfiguration
Verbose errors, missing headers
Security headers; no stack traces in prod
9
Improper Inventory
Shadow/old APIs exposed
OpenAPI docs; version and retire
10
Unsafe Consumption
Trusting external API responses
Validate responses; HTTPS; timeouts
Enterprise best practices
1. Treat OWASP API Top 10 as a checklist in design and code review. For every new endpoint, ask: does it check ownership (BOLA)? Does it require the right role/scope (function-level)? Does it limit input size and rate? Add security stories to your backlog.
2. Use middleware and policies for consistency. Authorization policies ([Authorize(Policy = "...")]), rate-limiting middleware, and security headers middleware mean you do not repeat the same checks in every controller. One place to update, one place to audit.
3. Centralise JWT validation and secrets. Use a single AddAuthentication().AddJwtBearer() configuration with Authority and Audience. Store secrets in Azure Key Vault or a secrets manager; reference them by name, not in code.
4. Log and alert on security events. Log failed auth, failed authorization (403), rate-limit hits (429), and suspicious patterns (enumeration, repeated bad input). Send alerts to your SIEM or Azure Monitor so you catch attacks early.
5. Run penetration tests and threat modelling. Automate security scans (e.g. OWASP ZAP, SonarQube SAST) in CI. Do threat modelling for new features: draw the data flow, identify trust boundaries, and map to OWASP risks. Fix findings before shipping.
6. Version and document. Use OpenAPI/Swagger for every API; version via URL or header; retire old versions with a deprecation period. Improper inventory is real: if you do not know what is exposed, you cannot secure it.
You can also explore more patterns in the API Architecture resource page.
Summary
The OWASP API Security Top 10 is a concrete checklist: BOLA, Broken Authentication, Object Property Authorization, Resource Consumption, Function Level Authorization, Sensitive Business Flows, SSRF, Security Misconfiguration, Inventory Management, and Unsafe Consumption—for each, enforce authorization per resource and per function, validate input and output, and harden configuration. Ignoring BOLA or returning entities instead of DTOs creates real vulnerabilities; in .NET, use JWT validation, DTOs, rate limiting, security headers, and Key Vault, then iterate with threat modelling and pen tests. Next, map the ten risks to your endpoints, fix BOLA and function-level auth first, then add rate limiting and security headers; use the FAQs below as a quick reference.
Position & Rationale
I treat BOLA as the first fix on every API: every endpoint that takes an object ID must check that the caller is allowed to access that object (e.g. order.UserId == currentUser.Id). I enforce function-level authorization so that “delete user” or “export data” is not callable by everyone. I use DTOs and avoid returning entities directly so we don’t leak properties (mass assignment, object property authorization). I add rate limiting and input validation so we don’t get DoS or injection. I avoid trusting the client for authorization (e.g. “role: admin” in the request body); the server must derive identity and permissions from the token or session. I don’t skip the “boring” items: security headers, inventory (OpenAPI, versioning), and logging security events.
Trade-Offs & Failure Modes
Strict authorization adds checks on every request; you gain protection from BOLA and function-level abuse. DTOs add mapping code; you gain control over what is exposed. Rate limiting can block legitimate bursts if misconfigured; tune by endpoint and client. Failure modes: checking “is authenticated?” but not “can this user access this resource?” (BOLA); exposing internal IDs or fields (object property); no rate limit (resource consumption); trusting client-supplied URLs in server-side requests (SSRF).
What Most Guides Miss
Most guides list the Top 10 but don’t stress that BOLA is the most common—fix “does this user own this resource?” first on every endpoint that takes an ID. Another gap: 403 vs 404—return 404 when the resource doesn’t exist or the user isn’t allowed (to avoid leaking existence); some prefer 403 when the user is not allowed but the resource exists; document your policy. Unsafe consumption (calling downstream APIs without validating responses or handling errors) is underplayed; your API is only as secure as the weakest service it calls.
Decision Framework
For every endpoint with an object ID → After load, check ownership or permission; return 404 or 403 as appropriate.
For every sensitive operation (delete, export, admin) → Enforce function-level authorization (role, scope, or policy).
For request/response → Use DTOs; validate input; don’t expose internal fields or accept mass assignment.
For configuration → Security headers, HTTPS, secrets in Key Vault; no default credentials or debug in production.
For inventory → OpenAPI for every API; version and deprecate; log and alert on auth failures and abuse.
Key Takeaways
BOLA first: authorise every request against the resource owner; then function-level auth for sensitive operations.
Use DTOs and validate input; rate limit and harden config.
403 vs 404: decide and document; avoid leaking existence.
Inventory (OpenAPI, versioning) and logging security events so you can detect and respond.
If you’re building or modernizing API platforms, I provide API architecture consulting covering API-first design, governance, and integration strategy.
When I Would Use This Again — and When I Wouldn’t
I’d use the OWASP API Top 10 again as a checklist for every new or refactored API—BOLA, auth, DTOs, rate limiting, config, inventory. I’d fix BOLA and broken authentication first; then object property, function-level, and the rest. I wouldn’t ship an API that takes resource IDs without ownership checks. I also wouldn’t treat the list as “one and done”; threat modelling and pen tests should revisit these risks as the API evolves.
Frequently Asked Questions
Frequently Asked Questions
What is OWASP?
OWASP (Open Web Application Security Project) is a nonprofit that publishes security standards and risk lists, including the API Security Top 10 and Web Application Top 10. Use their lists as checklists for design and code review.
What is BOLA?
Broken Object Level Authorization (BOLA), also known as IDOR, means the API does not check that the caller is allowed to access the specific resource (e.g. another user’s order). Mitigate by authorising every request against the resource owner: compare order.UserId with _currentUser.Id.
What is the difference between BOLA and Broken Function Level Authorization?
BOLA is about which object (e.g. order 123 vs 456) the user can access. Broken Function Level Authorization is about which operation (e.g. delete user, export data) the user can call. Both require server-side checks.
How do I validate JWT in .NET?
Use AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(...) with Authority, Audience, and TokenValidationParameters (ValidateIssuer, ValidateAudience, ValidateLifetime). Never skip signature validation.
Should I use API keys for user-facing APIs?
No. Use OAuth2/OpenID Connect and JWT for user authentication. API keys are hard to rotate per user and often leak. Use keys only for machine-to-machine or internal services, and store them in Key Vault or a secrets manager.
How do I prevent mass assignment in .NET?
Use explicit input DTOs with only the properties you allow. Do not bind request body directly to domain entities. Ignore unknown JSON properties; use [JsonIgnore] on sensitive entity properties.
What is rate limiting and why do I need it?
Rate limiting caps how many requests a client can send in a time window. It prevents abuse, DoS, and cost blowouts. Return 429 Too Many Requests and Retry-After when the limit is exceeded. Use middleware (e.g. AspNetCoreRateLimit) or Azure API Management.
What is SSRF and how do I prevent it?
Server Side Request Forgery (SSRF) is when the server makes a request to a URL supplied by the client, often to internal or metadata endpoints. Prevent by allowlisting hosts and schemes; block private IPs and metadata URLs (e.g. 169.254.169.254); do not trust client input for outbound URLs.
What security headers should I add?
At least Strict-Transport-Security (HSTS), X-Content-Type-Options: nosniff, and optionally Content-Security-Policy, X-Frame-Options. Add via middleware or web server config.
How do I document and version my API?
Use OpenAPI (Swagger) for documentation. Version via URL path (/api/v1/orders) or header (Api-Version: 1). Retire old versions with a deprecation period and communicate to clients.
What is “unsafe consumption of APIs”?
When your API calls another API and trusts its response without validation, or sends sensitive data in URLs or logs, or ignores TLS or timeouts. Mitigate by validating responses, using HTTPS, and applying timeouts and circuit breakers (e.g. Polly).
Should I return 403 or 404 when the user is not allowed to access a resource?
Return 404 when the resource does not exist (to avoid leaking existence). Return 403 Forbid when the resource exists but the user is not allowed—use this only when you do not leak information (e.g. admin views). Often 404 is safer for BOLA to avoid enumeration.
How do I enforce authorization per endpoint in .NET?
Use [Authorize(Roles = "Admin")], [Authorize(Policy = "RequireScope")], or resource-based authorization (IAuthorizationHandler with the resource). Always authorize on the server; never rely on hiding URLs.
What is the OWASP API Top 10 used for?
As a checklist for designing and reviewing APIs: ensure you have addressed each of the ten risks (authorization, authentication, data exposure, rate limiting, configuration, inventory, SSRF, unsafe consumption). Use it in threat modelling and security training.
How do I implement resource-based authorization in .NET?
Create a custom IAuthorizationRequirement (e.g. OrderOwnerRequirement) and an AuthorizationHandler<TRequirement, TResource> that checks ownership. Register the handler in DI, then call IAuthorizationService.AuthorizeAsync(user, resource, requirement) in your controller before returning the resource.
What tools can I use to automate OWASP checks?
Use OWASP ZAP (dynamic scanning), SonarQube or Semgrep (SAST), and Snyk or Dependabot (dependency scanning). Run these in CI so security issues are caught before deployment. Combine with manual penetration testing for full coverage.
Related Guides & Resources
Explore the matching guide, related services, and more articles.