Unicode Normalization in Aspnet with Mutual Tls
Unicode Normalization in Aspnet with Mutual Tls — how this specific combination creates or exposes the vulnerability
Unicode normalization becomes a security-relevant concern in ASP.NET when an application compares or processes identifiers—such as usernames, hostnames, or certificate common names—after they have passed through mutual TLS (mTLS) handshakes. In mTLS, the server requests a client certificate and validates it. If the application derives an identity (for example, a subject common name or a mapped user) from the certificate and then normalizes that string using different Unicode rules than the backend authorization store, an attacker can use normalization mismatches to impersonate a legitimate identity.
Consider an ASP.NET Core app that uses mTLS and maps certificate subject fields to application users. The certificate subject may contain Unicode characters (for example, Latin-1 or composed forms). If the app normalizes the subject using FormC while the authorization check uses FormD, an attacker can craft a certificate with a visually identical string that bypasses identity checks. This is a canonicalization issue across the mTLS boundary: the TLS layer presents a raw certificate subject, and the application layer performs inconsistent normalization before comparison.
Another scenario involves HTTP request headers or URL paths that include Unicode identifiers (such as usernames in headers) when mTLS is enforced. An attacker can send a request with a normalized form in the URL or header while the server compares it to a normalized canonical value stored in configuration or a database. Because mTLS ensures channel authenticity but does not guarantee consistent interpretation of Unicode, the mismatch can lead to authorization bypass or information disclosure. For example, a username éuser stored in normalized form may match éuser sent by the client if normalization is applied consistently; if not, the server might treat them as distinct identities, potentially escalating privileges or granting unintended access.
Input validation and property authorization checks in ASP.NET can be affected when Unicode normalization is applied inconsistently across the stack. An attacker may leverage this to bypass property-level authorization if identifiers are normalized differently between the incoming mTLS-authenticated request and the internal policy evaluation. Since mTLS binds the client identity to the request, inconsistent normalization can break the assumed binding and allow a malicious client to act as another authenticated user.
To detect this class of issue, scanning should compare how the application normalizes certificate subjects and identifiers with how it normalizes inputs used in authorization and property checks. Findings typically highlight missing canonicalization before comparison and inconsistent use of normalization forms across the mTLS boundary. Remediation focuses on enforcing a single normalization form early in the request processing pipeline and ensuring that all identity comparisons use the same form, particularly when mTLS presents identities from certificates.
Mutual Tls-Specific Remediation in Aspnet — concrete code fixes
Remediation for Unicode normalization issues in ASP.NET with mutual TLS centers on canonicalizing identities before comparison and ensuring consistent normalization across all layers. Below are concrete code examples that demonstrate how to implement this in ASP.NET Core with mTLS.
1. Normalize certificate subject before mapping to application identity
When using client certificates, extract the subject and normalize it to a canonical form (typically FormC or FormD) before mapping to users or roles. This prevents mismatches between the certificate subject and stored identities.
using System.Globalization;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Http;
public static class CertificateExtensions
{
public static string GetNormalizedSubject(this HttpRequest request, UnicodeNormalization normalization = UnicodeNormalization.FormC)
{
var cert = request.HttpContext.Connection.ClientCertificate;
if (cert == null) return null;
var subject = cert.Subject;
return Normalize(subject, normalization);
}
private static string Normalize(string value, UnicodeNormalization normalization)
{
return normalization == UnicodeNormalization.FormC
? value.Normalize(NormalizationForm.FormC)
: value.Normalize(NormalizationForm.FormD);
}
public enum UnicodeNormalization { FormC, FormD }
}
2. Apply consistent normalization in authorization policies
Define an authorization handler that normalizes both the current identity and the required value using the same form. This ensures that comparisons are canonical regardless of how the client sends data.
using System.Globalization;
using System.Security.Claims;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
public class NormalizedNameRequirement : IAuthorizationRequirement
{
public string RequiredName { get; }
public NormalizedNameRequirement(string requiredName) => RequiredName = requiredName;
}
public class NormalizedNameHandler : AuthorizationHandler<NormalizedNameRequirement, ClaimsPrincipal>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, NormalizedNameRequirement requirement, ClaimsPrincipal user)
{
var userIdentity = user.FindFirst("normalized_subject")?.Value;
var normalizedRequired = requirement.RequiredName.Normalize(NormalizationForm.FormC);
if (userIdentity != null && userIdentity.Equals(normalizedRequired, StringComparison.Ordinal))
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
3. Middleware to enforce normalization early in the pipeline
Insert middleware that normalizes relevant identifiers from the certificate and headers before routing and authorization so that downstream components see a consistent representation.
using System.Globalization;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
public class UnicodeNormalizationMiddleware
{
private readonly RequestDelegate _next;
public UnicodeNormalizationMiddleware(RequestDelegate next) => _next = next;
public async Task Invoke(HttpContext context)
{
var cert = context.Connection.ClientCertificate;
if (cert != null)
{
var normalized = cert.Subject.Normalize(NormalizationForm.FormC);
context.Items["NormalizedSubject"] = normalized;
}
// Optionally normalize a header if your protocol uses it
if (context.Request.Headers.TryGetValue("X-User-Id", out var userIdHeader))
{
context.Request.Headers["X-User-Id"] = userIdHeader.ToString().Normalize(NormalizationForm.FormC);
}
await _next(context);
}
}
// In Startup.Configure:
// app.UseMiddleware<UnicodeNormalizationMiddleware>();
4. Configure mTLS in ASP.NET Core
Ensure the server requests and validates client certificates. The following configuration enforces mTLS and makes the certificate available to the app for identity extraction.
// Program.cs or Startup.cs
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.ConfigureHttpsDefaults(httpsOptions =>
{
httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
httpsOptions.AllowedCipherSuites = new[] { /* restrict to strong ciphers if desired */ };
httpsOptions.ClientCertificateValidation = (cert, chain, errors) =>
{
// Basic validation; extend with custom checks as needed
return errors == SslPolicyErrors.None;
};
});
});
// Optionally map the certificate to a user or claims here
var app = builder.Build();
app.UseAuthentication();
app.UseAuthorization();
5. Map normalized subject to user or roles
After normalization, map the canonical subject to an application user or role to enforce permissions consistently.
app.Use(async (context, next) =>
{
var normalized = context.Items["NormalizedSubject"] as string;
if (!string.IsNullOrEmpty(normalized))
{
// Example mapping: replace with your user resolution logic
var user = await GetUserByNormalizedSubject(normalized);
if (user != null)
{
var identity = new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, user.UserName) });
context.User.AddIdentity(identity);
}
}
await next();
});
Key takeaways
- Normalize certificate subjects and identifiers to a single Unicode form (preferably FormC) before comparison.
- Apply normalization consistently across middleware, authorization handlers, and data stores.
- Use mTLS to bind client identity to requests, and treat the normalized certificate subject as the authoritative identity source.