mTLS & ASP.NET
In Microsoft, we use mTLS (Mutual Transport Layer Security) extensively to secure our service-to-service (S2S) communications. In this short post, I’m going to cover what mTLS is and how it can be used in dotnet.
Link: mTLS protocol explained in TLS RFC doc.
What is mTLS?
One of the key functions of the https protocol is to authenticate the server. This ensures that the client can trust the connection and be confident that it has not been intercepted or redirected to an attacker's server. The key difference between HTTPS and mTLS is that mTLS validates the identity of both parties, ensuring that they are who they claim to be.
Client connects to server
Server presents its TLS certificate
Client verifies the server's certificate
Client presents its TLS certificate
Server verifies the client's certificate
Server grants access
Client and server exchange information over encrypted TLS connection
The mTLS protocol is used in server-to-server (S2S) communication. For instance, within Microsoft, a single client request might pass through multiple services:
Client → Service A → Service B → Service C
When Service A makes an HTTP call to Service B, it needs to validate the identity of the destination. Simultaneously, Service B must validate the identity of Service A to ensure it is not being called by an unauthorized or untrusted service.
Drawback
The downside of mTLS is that it establishes full trust between services. Consequently, if Service A is compromised, an attacker could make calls to Service B, which would have no way of knowing if the calls are legitimate. This means an attacker could gain extensive access to all services by exploiting just one service. An alternative is to use token-based approach like OBO and PoP.
Enable mTLS Authentication in ASP.NET Core
The mTLS negotiation is managed by the web server, so the steps you need to take will vary depending on the web server you use. For IIS and Http.sys, you can refer to the ASP.NET documentation. Here, I'll outline the steps for Kestrel.
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<KestrelServerOptions>(options =>
{
options.ConfigureHttpsDefaults(options =>
options.ClientCertificateMode = ClientCertificateMode.RequireCertificate);
});
mTLS Renegotiation
TLS renegotiation is a process by which the client and server can re-assess the encryption requirements for an individual connection, including requesting a client certificate if not previously provided. TLS renegotiation is a security risk and isn't recommended because:
In HTTP/1.1 the server must first buffer or consume any HTTP data that is in flight such as POST request bodies to make sure the connection is clear for the renegotiation. Otherwise the renegotiation can stop responding or fail.
HTTP/2 and HTTP/3 explicitly prohibit renegotiation.
There are security risks associated with renegotiation. TLS 1.3 removed renegotiation of the whole connection and replaced it with a new extension for requesting only the client certificate after the start of the connection. This mechanism is exposed via the same APIs and is still subject to the prior constraints of buffering and HTTP protocol versions.
Renegotiation is disabled by default. You can also use ClientCertificateMode.AllowCertificate
to enable mTLS renegotiation. The AllowCertificate
mode enables renegotiating after the start of a connection to acquire a client certificate. This feature has been added in .NET 6.
Just note that to enable mTLS on HttpSys on Windows, you should enable clientcertnegotiation
via netsh.exe command:
netsh http add sslcert clientcertnegotiation=enable/disable
Validate Client Certificate
Once a request is received, you can write authentication/authorization handler to validate the certificate. The first step is retrieving the certificate and next we should validate the cert’s chain and its thumbprint.
var cert = await Context.Connection.GetClientCertificateAsync();
if (cert == null)
{
// return forbidden;
}
// Validate the cert's chain and its thumbprint
Configure HttpClient
On the client side, you need to configure the HttpClient
instance to provide the certificate in the mTLS negotiation process.
httpClientBuilder.ConfigurePrimaryHttpMessageHandler((s) =>
{
var clientHandler = new HttpClientHandler
{
CheckCertificateRevocationList = true,
};
clientHandler.ClientCertificates.Add(certificate);
return clientHandler;
});
Conclusion
In this article, we explore the use of mutual TLS (mTLS) for securing service-to-service communications, particularly within Microsoft's infrastructure. We discuss the key differences between HTTPS and mTLS, such as mutual identity validation, and the potential drawback of establishing full trust between services. The article also provides guidance on implementing mTLS in ASP.NET Core using Kestrel, discusses the security risks of TLS renegotiation, and outlines steps for client certificate validation and HttpClient configuration.
Subscribe to my newsletter
Read articles from Ehsan Mirsaeedi directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by