Bola Idor in Aspnet with Mutual Tls
Bola Idor in Aspnet with Mutual Tls — how this specific combination creates or exposes the vulnerability
Broken Object Level Authorization (BOLA) is an API security risk where an attacker can access or modify objects they should not have permission to touch. In ASP.NET APIs, BOLA often appears when object IDs (e.g., /orders/123) are predictable and authorization checks are incomplete or inconsistent. Adding mutual TLS (mTLS) changes the threat surface but does not automatically fix authorization logic; it can even create a false sense of security.
With mTLS, the server requests a client certificate during the TLS handshake and validates it. This strongly authenticates the client (machine or service) but does not by itself enforce object-level permissions. If an endpoint relies only on the presence of a valid client certificate and does not verify that the certificate’s associated identity has rights to a specific resource, BOLA persists. For example, an endpoint like GET /api/orders/{id} might verify that a client cert exists, but it may fail to check that the order belongs to the tenant or user implied by the certificate’s claims or mapped identity.
A common pattern in ASP.NET is to use certificate information (such as the subject or thumbprint) to identify a service or device, then map that to a tenant or scope. If the mapping between certificate and tenant is incomplete or if developers mistakenly treat mTLS as sufficient authorization, BOLA becomes likely. Consider a case where the certificate identifies Company A, but the endpoint does not ensure that the requested resource’s CompanyId matches the certificate’s CompanyId. Because mTLS happens before the application authorization logic, an attacker could iterate over IDs and access objects belonging to other tenants, even though each request presents a valid client certificate.
Another subtlety involves endpoint design. In ASP.NET, you might use route parameters (e.g., {id}) or bind from query strings. If authorization filters or policies do not explicitly compare the resource’s owning tenant to the certificate-derived tenant, BOLA is reachable. This can be compounded by misconfigured policy evaluation order, where resource-based checks run after per-endpoint authentication checks that only validate the certificate. Attackers can exploit this by leveraging predictable IDs across tenants, especially when IDs are not globally unique or when tenant boundaries are not enforced at the query level.
To detect this during a scan, middleBrick tests unauthenticated attack surfaces and, where possible, validates that authorization is enforced per object rather than per connection. In environments using mTLS, it checks whether authorization logic incorporates identity-bound attributes (such as tenant ID from certificate claims) when making access decisions. Without those checks, a valid client certificate does not prevent horizontal privilege escalation across objects that should be isolated.
Remediation involves ensuring that every object access includes a tenant or ownership check that references the identity derived from the client certificate. Do not assume that mTLS alone suffices. Combine endpoint authentication with resource-level authorization, and validate that the certificate-mapped identity matches the resource’s owning scope in all data access paths.
Mutual Tls-Specific Remediation in Aspnet — concrete code fixes
Secure ASP.NET APIs with mTLS by combining certificate validation with explicit object-level authorization. Below are concrete patterns and code examples to prevent BOLA while preserving mTLS benefits.
- Configure Kestrel to require client certificates and map claims in Program.cs:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ListenAnyIP(5000, listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
{
httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
httpsOptions.AllowedCipherSuites = new[] { /* restrict to strong ciphers if desired */ };
// Certificate validation can be extended via RemoteCertificateValidationCallback
});
});
});
// You can also add custom certificate validation for mapping
builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.RevocationMode = System.Security.Cryptography.X509Certificates.X509RevocationMode.NoCheck; // adjust per policy
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var cert = context.ClientCertificate;
if (cert == null)
{
context.Fail("No certificate provided");
return Task.CompletedTask;
}
// Example: extract tenant from certificate subject or extended properties
var tenantId = ExtractTenantFromCertificate(cert);
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, context.Principal.Identity?.Name ?? cert.Thumbprint),
new Claim("tenant_id", tenantId)
};
context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
return Task.CompletedTask;
}
};
});
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
string ExtractTenantFromCertificate(X509Certificate2 cert)
{
// Implement mapping logic, e.g., parse Subject or use ExtendedKeyUsage/OIDs
// This is a simplified example
return cert.Subject.Contains("Tenant=A") ? "A" : "unknown";
}
- Enforce tenant/resource ownership in controllers or via a global authorization policy:
[ApiController]
[Route("api/[controller]")]
public class OrdersController : ControllerBase
{
private readonly IOrderRepository _repository;
public OrdersController(IOrderRepository repository)
{
_repository = repository;
}
[HttpGet("{id}")]
[Authorize]
public async Task<IActionResult> GetOrder(Guid id)
{
var tenantId = User.FindFirstValue("tenant_id");
if (string.IsNullOrEmpty(tenantId))
{
return Forbid();
}
var order = await _repository.GetOrderAsync(id, tenantId);
if (order == null)
{
return NotFound();
}
// Ensure the retrieved order belongs to the tenant derived from the client certificate
return Ok(order);
}
}
public interface IOrderRepository
{
Task<Order> GetOrderAsync(Guid id, string tenantId);
}
// Example repository implementation using parameterized queries to enforce tenant scope
public class OrderRepository : IOrderRepository
{
private readonly IDbConnection _db;
public OrderRepository(IDbConnection db) => _db = db;
public async Task<Order> GetOrderAsync(Guid id, string tenantId)
{
const string sql = "SELECT * FROM Orders WHERE Id = @Id AND TenantId = @TenantId";
return await _db.QueryFirstOrDefaultAsync<Order>(sql, new { Id = id, TenantId = tenantId });
}
}
- Apply a global authorization policy to require tenant claims and enforce object ownership consistently:
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("RequireTenantOwnership", policy =>
{
policy.RequireAuthenticatedUser();
policy.RequireClaim("tenant_id");
// You can add more requirements as needed
});
});
// Then use [Authorize(Policy = "RequireTenantOwnership")] on sensitive endpoints
These steps ensure that mTLS provides strong client authentication while object-level checks enforce tenant boundaries, effectively mitigating BOLA in ASP.NET APIs.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |