HIGH insecure direct object referenceaspnetmutual tls

Insecure Direct Object Reference in Aspnet with Mutual Tls

Insecure Direct Object Reference in Aspnet with Mutual Tls — how this specific combination creates or exposes the vulnerability

Insecure Direct Object Reference (BOLA/IDOR) occurs when an API exposes internal object references—such as identifiers, keys, or database row IDs—without verifying that the requesting identity is authorized to access that specific resource. In ASP.NET APIs, this commonly manifests when endpoints like /api/users/{userId} or /api/orders/{orderId} trust client-supplied identifiers directly, performing only ownership checks against the authenticated user’s claims or session state.

Mutual TLS (mTLS) adds a strong layer of identity assurance by requiring clients to present a valid certificate during the TLS handshake. The server validates the certificate and often maps it to a principal, which can be used for authentication. However, mTLS does not inherently enforce authorization. When mTLS is used for authentication only, developers may incorrectly assume that because a certificate is valid, the associated identity is automatically authorized for every object they reference. This false sense of security can lead to relaxed authorization checks, making BOLA/IDOR more likely: an attacker who controls a valid client certificate can still iterate over IDs and access other users’ data if the server does not enforce proper ownership and scope checks.

In ASP.NET, this risk is amplified when authorization logic relies solely on the certificate subject or claim without cross-referencing the resource’s owning tenant or relationship. For example, a healthcare API using mTLS to authenticate devices might expose patient records via /api/patients/{patientId}. If the endpoint validates the certificate maps to a device but does not confirm that the device is scoped to the requested patientId, an attacker with a valid device certificate can enumerate patient IDs and retrieve records belonging to other patients. The combination of mTLS authentication and missing per-request authorization creates a BOLA/IDOR vulnerability despite the presence of strong transport-layer identity.

Another scenario involves role- or group-based access where mTLS maps to a group claim, but object-level permissions are not checked. An endpoint might allow GET /api/projects/{projectId} if the certificate belongs to a group named ProjectReaders, without verifying whether the specific project is within that group’s allowed scope. Because mTLS certificates can be long-lived and reused, and because the attack surface includes unauthenticated scans in some configurations, attackers can probe IDs systematically to discover accessible resources. The OWASP API Top 10 lists BOLA/IDOR as a critical risk, and real-world incidents have shown attackers exploiting such gaps to access sensitive data across tenants.

To detect this using middleBrick’s 12 security checks—executed in parallel within 5–15 seconds—scan an ASP.NET endpoint with mTLS enabled but weak or missing object-level authorization. The scanner’s BOLA/IDOR check will flag endpoints where resource identifiers are directly exposed without verifying tenant, ownership, or scope, even when mTLS is used for transport-layer identity. Findings include severity ratings and remediation guidance mapped to frameworks such as OWASP API Top 10 and help prioritize fixes.

Mutual Tls-Specific Remediation in Aspnet — concrete code fixes

Remediation centers on enforcing strict authorization at the resource level after mTLS authentication. In ASP.NET Core, use policy-based authorization with requirements that validate the relationship between the certificate identity and the requested object. Avoid relying on the certificate alone to grant access to a specific resource ID.

Example: Require that a device certificate’s subject or a custom claim includes the tenant or scope, and verify that the requested resource belongs to that scope.

// Program.cs or Startup configuration
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("RequireTenantScope", policy =>
    {
        policy.RequireAuthenticatedUser();
        policy.Requirements.Add(new TenantScopeRequirement());
    });
});

builder.Services.AddSingleton<IAuthorizationHandler, TenantScopeHandler>();

// TenantScopeRequirement.cs
public class TenantScopeRequirement : IAuthorizationRequirement { }

// TenantScopeHandler.cs
public class TenantScopeHandler : AuthorizationHandler<TenantScopeRequirement, object>
{
    protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, TenantScopeRequirement requirement, object resource)
    {
        // resource could be a DTO containing the tenant ID from the endpoint route
        if (resource is not ResourceWithTenant resourceWithTenant) return;

        // Extract tenant from certificate claim (configured in authentication)
        var tenantClaim = context.User.FindFirst("tenant_id");
        if (tenantClaim == null) return;

        if (tenantClaim.Value == resourceWithTenant.TenantId)
        {
            context.Succeed(requirement);
        }
    }
}

Use endpoint filters or action filters to enforce object-level checks consistently. For example, in a controller retrieving a sensitive record:

[ApiController]
[Route("api/[controller]")]
[Authorize(Policy = "RequireTenantScope")]
public class PatientsController : ControllerBase
{
    private readonly AppDbContext _context;

    public PatientsController(AppDbContext context)
    {
        _context = context;
    }

    [HttpGet("{patientId}")]
    public async Task<IActionResult> GetPatient(Guid patientId)
    {
        var tenantId = User.FindFirst("tenant_id")?.Value;
        if (string.IsNullOrEmpty(tenantId))
        {
            return Forbid();
        }

        var patient = await _context.Patients
            .Where(p => p.Id == patientId && p.TenantId == Guid.Parse(tenantId))
            .FirstOrDefaultAsync();

        if (patient == null)
        {
            return NotFound();
        }

        return Ok(patient);
    }
}

Configure mTLS in ASP.NET Core via Kestrel:

// Program.cs
var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ListenAnyIP(5001, listenOptions =>
    {
        listenOptions.UseHttps(httpsOptions =>
        {
            httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
            httpsOptions.AllowedClientCertificates.Add(new X509Certificate2("client-ca.crt"));
            // Optionally set revocation mode
            httpsOptions.ClientCertificateRevocationCheck = Context.RevocationMode.Online;
        });
    });
});

// Add authentication from client certificate
builder.Services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
    .AddCertificate(options =>
    {
        options.AllowedCertificateTypes = CertificateTypes.All;
        options.RevocationMode = X509RevocationMode.Online;
        options.RoleClaimType = ClaimTypes.Role;
        options.Events = new CertificateAuthenticationEvents
        {
            OnCertificateValidated = context =>
            {
                // Map certificate to claims, e.g., tenant_id
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, context.ClientCertificate.SubjectName.Name),
                    new Claim("tenant_id", ExtractTenantFromSubject(context.ClientCertificate.SubjectName.Name))
                };
                var identity = new ClaimsIdentity(claims, context.Scheme.Name);
                context.Principal = new ClaimsPrincipal(identity);
                context.Success();
                return Task.CompletedTask;
            }
        };
    });

app.UseAuthentication();
app.UseAuthorization();

string ExtractTenantFromSubject(string subject)
{
    // Example: extract tenant_id from CN=tenant-123,O=Example
    var match = Regex.Match(subject, @"CN=tenant-([^,]+)");
    return match.Success ? match.Groups[1].Value : string.Empty;
}

With these patterns, mTLS provides strong client authentication while per-request authorization ensures that a valid certificate does not automatically imply access to every object. middleBrick’s LLM/AI Security checks can further validate that endpoints with mTLS are not subject prompt leakage or unsafe consumption patterns, and its dashboard helps track risk scores as you apply these fixes.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

Does mutual TLS prevent IDOR if authorization checks are missing?
No. Mutual TLS authenticates the client but does not enforce object-level authorization. Without explicit checks that the authenticated identity is allowed to access the specific resource ID, BOLA/IDOR can still occur.
How can I verify my ASP.NET endpoints are protected against IDOR when using mTLS?
Use middleBrick’s scanner to run a black-box check against endpoints that use mTLS. It tests whether resource identifiers are exposed without proper tenant or ownership validation and provides prioritized findings with remediation guidance.