HIGH aspnetidor enumeration

Idor Enumeration in Aspnet

How IDOR Enumeration Manifests in ASP.NET

Insecure Direct Object Reference (IDOR) enumeration in ASP.NET APIs typically arises when an endpoint exposes internal resource identifiers (like database primary keys) without verifying that the authenticated user has permission to access the specific object referenced by that identifier. ASP.NET MVC and Web API patterns are particularly susceptible due to common development patterns.

A classic vulnerable pattern is a controller action that accepts an id parameter from the route or query string and uses it directly in an Entity Framework query without a user-specific authorization check:

[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
    private readonly AppDbContext _context;
    
    public OrdersController(AppDbContext context) => _context = context;
    
    // VULNERABLE: No check that order belongs to current user
    [HttpGet("{id}")]
    public async Task> GetOrder(int id)
    {
        var order = await _context.Orders.FindAsync(id);
        if (order == null) return NotFound();
        return Ok(order);
    }
}

An attacker who knows or guesses sequential IDs (e.g., /api/orders/1, /api/orders/2) can enumerate all orders by iterating through IDs. This is exacerbated by ASP.NET's default model binding, which automatically converts route parameters to types like int, making such endpoints trivial to probe. The vulnerability is compounded if the API also lacks rate limiting (one of middleBrick's 12 checks), allowing rapid enumeration.

Another manifestation involves complex object graphs. Consider an endpoint that loads a related entity without validating the parent resource's ownership:

[HttpGet("{orderId}/items/{itemId}")]
public async Task> GetOrderItem(int orderId, int itemId)
{
    // VULNERABLE: Only checks item exists, not that item.Order.UserId == CurrentUser.Id
    var item = await _context.OrderItems
        .Include(i => i.Order)
        .FirstOrDefaultAsync(i => i.Id == itemId && i.OrderId == orderId);
    return item != null ? Ok(item) : NotFound();
}

Here, an attacker could manipulate orderId and itemId to access items from orders they do not own, even if the itemId is not guessable. This is a form of Horizontal Privilege Escalation, covered under middleBrick's BOLA/IDOR and Property Authorization checks.

ASP.NET Core's [Authorize] attribute alone is insufficient—it only validates authentication, not resource ownership. The flaw is a business logic error in the data access layer, specific to how developers wire up Entity Framework (or other ORMs) with controller actions.

ASP.NET-Specific Detection

Detecting IDOR enumeration in ASP.NET requires testing for unauthorized access to resources through parameter manipulation. Since the vulnerability exists at the API runtime level, static code analysis alone is often insufficient; dynamic testing of the live endpoint is essential.

Manual testing involves: 1) Authenticating as a low-privilege user, 2) capturing a valid request to an endpoint like GET /api/orders/123, 3) replaying the request with altered id values (e.g., 124, 125, 999) while maintaining the same session, and 4) observing if the API returns data belonging to another user (HTTP 200 with different owner) versus proper denials (HTTP 403/404). However, this is time-consuming and easy to miss.

middleBrick automates this detection via its BOLA/IDOR check. When you submit an ASP.NET API URL, middleBrick's scanner performs black-box testing: it first identifies endpoints that accept identifiers (by parsing OpenAPI specs if available, or by heuristic discovery). For each such endpoint, it sends sequential requests with mutated IDs (incrementing, decrementing, common default values like 1, 0, -1) and analyzes responses. It looks for:

  • HTTP 200 responses with valid data when the resource should be inaccessible
  • Differences in response payloads (e.g., userId field changes) indicating access to another user's object
  • Timing or content discrepancies between 404 Not Found (proper) and 200 OK with empty/error data (potential blind enumeration)

For ASP.NET APIs that expose OpenAPI/Swagger specs (common with Swashbuckle), middleBrick resolves all $ref definitions to understand parameter schemas, improving accuracy. The scanner does not require credentials for the unauthenticated attack surface check, but if you provide a session token (e.g., via the CLI's --header "Authorization: Bearer <token>"), it will test authenticated IDOR as well.

Example using the middleBrick CLI to scan an ASP.NET API with authentication:

middlebrick scan https://api.example.com \
  --header "Authorization: Bearer eyJhbGciOiJIUzI1NiIs..."

The resulting report will flag any BOLA/IDOR findings with the specific endpoint, manipulated parameter, and evidence payloads, mapping the issue to OWASP API Top 10:2023 B01: Broken Object Level Authorization.

ASP.NET-Specific Remediation

Remediating IDOR in ASP.NET requires implementing resource-based authorization checks at the data access layer. The goal is to ensure that every query filtering by user-controlled ID also filters by the current user's identity or their permitted roles.

1. Use Resource-Based Authorization with IAuthorizationService
Asp.NET Core provides a powerful pattern where you load the resource first, then explicitly authorize access to that specific instance. This is the recommended approach over global filters because it handles complex ownership logic.

[HttpGet("{id}")]
public async Task> GetOrder(int id)
{
    var order = await _context.Orders.FindAsync(id);
    if (order == null) return NotFound();
    
    // AUTHORIZATION CHECK: Verify current user owns this order
    var authorizationResult = await _authorizationService.AuthorizeAsync(
        User, order, "OrderOwnerPolicy");
    
    if (!authorizationResult.Succeeded)
        return Forbid(); // or NotFound() to avoid enumeration
    
    return Ok(order);
}

You must register a policy and a corresponding requirement handler:

public class OrderOwnerRequirement : IAuthorizationRequirement { }

public class OrderOwnerHandler : AuthorizationHandler<OrderOwnerRequirement, Order>
{
    protected override Task HandleRequirementAsync(
        AuthorizationHandlerContext context, 
        OrderOwnerRequirement requirement, 
        Order order)
    {
        // Assuming User.Identity.Name is the user's unique identifier
        if (order.UserId == context.User.Identity.Name)
            context.Succeed(requirement);
        
        return Task.CompletedTask;
    }
}

// In Program.cs / Startup.cs
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("OrderOwnerPolicy", policy =>
        policy.Requirements.Add(new OrderOwnerRequirement()));
});
builder.Services.AddSingleton<IAuthorizationHandler, OrderOwnerHandler>();

2. Scope Queries to the Current User at the Database Level
Even better, modify your EF queries to always filter by UserId before applying the ID filter. This prevents accidental leaks if you forget an authorization check elsewhere.

[HttpGet("mine")]
public async Task>> GetMyOrders()
{
    // Query is automatically scoped to current user
    var userId = User.Identity.Name;
    var orders = await _context.Orders
        .Where(o => o.UserId == userId)
        .ToListAsync();
    return Ok(orders);
}

// For single-item endpoints, combine scoping with existence check:
[HttpGet("{id}")]
public async Task> GetOrder(int id)
{
    var userId = User.Identity.Name;
    var order = await _context.Orders
        .FirstOrDefaultAsync(o => o.Id == id && o.UserId == userId);
    
    return order != null ? Ok(order) : NotFound();
}

3. Avoid Sequential IDs in Public APIs
Consider using non-guessable identifiers (GUIDs/UUIDs) for public-facing resource IDs. While not a complete fix (an attacker might still obtain a valid GUID from another endpoint), it raises the bar for enumeration. In ASP.NET, you can configure EF to use Guid keys: builder.Property(o => o.Id).HasDefaultValueSql("NEWID()");.

4. Return 404 Not Found for Unauthorized Access
To prevent user enumeration, do not return 403 Forbidden when a resource exists but belongs to another user. Instead, return 404 with a generic message. This makes it harder for an attacker to distinguish between "resource doesn't exist" and "resource exists but you can't see it."

These fixes address the root cause by ensuring that every data access operation respects the principle of least privilege at the resource level. After applying fixes, rescan your API with middleBrick to verify the BOLA/IDOR finding is resolved.

Frequently Asked Questions

Why is IDOR enumeration particularly dangerous in ASP.NET APIs?
ASP.NET's convention-based routing and automatic model binding make it easy to create endpoints that accept raw IDs. Combined with Entity Framework's simplicity, developers often write queries like _context.Orders.FindAsync(id) without coupling the filter to the current user's identity. This creates a large attack surface where sequential or predictable IDs (common with integer primary keys) allow attackers to enumerate entire datasets, leading to massive data breaches.
How does middleBrick detect IDOR without valid user credentials?
middleBrick's unauthenticated scanning tests the public attack surface. For endpoints that do not require authentication (e.g., public product catalogs), it checks if resource IDs are guessable and if the API returns data for arbitrary IDs. For authenticated endpoints, you can provide a session token (via CLI --header or dashboard upload), and middleBrick will perform authenticated IDOR tests by manipulating IDs while maintaining that session, directly simulating an attacker with a low-privilege account.