Idor Enumeration in Aspnet (Csharp)
Idor Enumeration in Aspnet with Csharp — how this specific combination creates or exposes the vulnerability
In ASP.NET applications written in C#, IDOR (Insecure Direct Object References) enumeration occurs when an API endpoint exposes numeric or predictable identifiers and fails to enforce proper authorization checks per request. For example, an endpoint like /api/orders/{id} may return HTTP 200 for valid objects but also reveals sequential IDs and whether a given ID exists based on response differences (e.g., 200 vs 404). When C# controller methods directly use route or query parameters to fetch records without confirming that the current user owns or is permitted to access that specific record, the unauthenticated or insufficiently authorized attacker can systematically iterate IDs to enumerate accessible resources. This is IDOR enumeration, and it commonly maps to the OWASP API Top 10 A01: Broken Object Level Authorization.
ASP.NET’s default behavior for route parameters is to bind values to method parameters without context-aware authorization. If the C# code queries a database using something like _context.Orders.Find(id) and returns the entity without verifying tenant or ownership, the endpoint becomes an enumeration vector. Distinguishing between a missing object and an unauthorized object often relies on status codes: a 404 suggests the ID does not exist or is inaccessible, while a 200 reveals it exists and is readable. Attackers exploit this difference to map the ID space, a behavior detectable in middleBrick’s BOLA/IDOR checks. Because the scan is unauthenticated, it sends sequential and random IDs to observe status code patterns, confirming whether the API leaks existence or count information. Such enumeration can lead to further privilege escalation or data exposure when combined with other flaws.
In C# ASP.NET, the risk is amplified when controllers rely on implicit trust in identifiers, especially if the API also exposes OpenAPI specs with path parameters but omits authorization details in the spec. middleBrick’s OpenAPI/Swagger analysis resolves $ref definitions and cross-references spec definitions with runtime findings, highlighting mismatches where an operation claims to require no authentication yet uses sensitive identifiers. Without complementary runtime checks, developers may assume security based on spec annotations alone. The scanner’s parallel checks, including Authentication and Property Authorization, surface these gaps by comparing declared security schemes to actual responses, ensuring that IDOR enumeration vectors are identified before attackers map sensitive datasets.
Csharp-Specific Remediation in Aspnet — concrete code fixes
To remediate IDOR enumeration in C# ASP.NET, enforce per-request authorization by validating ownership or tenant context before returning any data. Avoid returning different status codes for unauthorized versus non-existent resources when possible; use a consistent 403 or 404 pattern for IDs the caller should not know about. Prefer GUIDs or opaque identifiers instead of sequential integers to reduce predictability, and always verify permissions against the business logic layer rather than relying on route data alone.
Code example: Safe lookup with authorization
// OrderController.cs
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly AppDbContext _context;
private readonly IUserContext _userContext;
public OrdersController(AppDbContext context, IUserContext userContext)
{
_context = context;
_userContext = userContext;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetOrder(Guid id)
{
// Ensure the user can access this order
var order = await _context.Orders
.FirstOrDefaultAsync(o => o.Id == id && o.OwnerId == _userContext.UserId);
if (order == null)
{
// Use a consistent response to avoid enumeration
return NotFound();
}
return Ok(order);
}
}
Code example: Using a service for authorization
// OrderAuthorizationService.cs
public class OrderAuthorizationService
{
private readonly AppDbContext _context;
private readonly IUserContext _userContext;
public OrderAuthorizationService(AppDbContext context, IUserContext userContext)
{
_context = context;
_userContext = userContext;
}
public async Task<bool> CanAccessOrderAsync(Guid orderId)
{
return await _context.Orders
.AnyAsync(o => o.Id == orderId && o.OwnerId == _userContext.UserId);
}
}
// OrdersController.cs
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly OrderAuthorizationService _authService;
public OrdersController(OrderAuthorizationService authService)
{
_authService = authService;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetOrder(Guid id)
{
if (!await _authService.CanAccessOrderAsync(id))
{
return Forbid();
}
var order = await _context.Orders.FindAsync(id);
if (order == null)
{
return NotFound();
}
return Ok(order);
}
}
Code example: Mitigating enumeration with consistent responses
// OrdersController.cs
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly AppDbContext _context;
private readonly IUserContext _userContext;
public OrdersController(AppDbContext context, IUserContext userContext)
{
_context = context;
_userContext = userContext;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetOrder(Guid id)
{
var order = await _context.Orders
.FirstOrDefaultAsync(o => o.Id == id && o.OwnerId == _userContext.UserId);
// Always return 404 for both missing and unauthorized records to prevent enumeration
if (order == null)
{
return NotFound(new { Message = "Not found." });
}
return Ok(order);
}
}
Additional recommendations
- Use policy-based authorization (e.g., ASP.NET Core policies) to centralize access rules.
- Validate identifiers format early in the pipeline to avoid unnecessary database queries.
- Ensure OpenAPI specs reflect the actual authorization requirements to avoid discrepancies flagged by middleBrick’s spec analysis.