Supporting SSL Acceleration (Offloading) in ASP.NET Core
Recently I needed to add support for SSL Acceleration (Offloading) to one of projects I'm working on. In ASP.NET MVC this usually meant custom RequireHttpsAttribute
, URL generator and IsHttps
method. Whole team needed to be aware that custom components must be used instead of the ones provided by framework, otherwise the things would break. This is no longer case for ASP.NET Core, thanks to low level APIs like request features there is a more elegant way.
SSL Acceleration (Offloading)
SSL Acceleration is a process of using a hardware accelerator for performing SSL decryption and/or decryption. The process usually takes place on a load balancer or firewall, in which case it's called SSL Offloading. There are two flavors off SSL Offloading: SSL Bridging and SSL Termination. SSL Bridging usually doesn't require anything specific from application, but SSL Termination does. In case of SSL Termination the SSL connection doesn't go beyond the SSL Accelerator. The are two main benefits from SSL Termination:
- Improved performance (the web servers don't have to use resources for SSL processing)
- Simplified certificate management (the certificates are managed on a single device instead of every web server in cluster)
The drawback is that HTTPS traffic is not reaching the application. In this context the performance benefit can be questioned. The application is no longer able to fully utilize some of HTTP/2 features (for example Server Push) while the resources gain might not be that significant as modern CPUs have good support for encryption/decryption.
Despite the fact that SSL is being terminated, the application still must be able to verify if the original request was made over HTTPS (otherwise it could lower the application security). Typically the SSL Accelerators are providing information about the original protocol through dedicated HTTP header (one quite popular is X-Forwarded-Proto
) which application needs to properly interpret.
Making ASP.NET Core understand SSL Acceleration
The "properly interpret" means that application needs to detect the presence of the header and if the value indicates that original request was over HTTPS it should be treated as such. In case of ASP.NET Core the perfect behavior would be for HttpContext.Request.IsHttps
to return true. This would automatically make RequireHttpsAttribute
and AddRedirectToHttps
from URL Rewriting Middleware behave correctly. Also any other code which depends on that property will keep working as expected.
Luckily the value of HttpContext.Request.IsHttps
is based on IHttpRequestFeature.Scheme
property which value can be changed by application. Assuming that the header name is X-Forwarded-Proto
and its value is the original scheme in lower case, following snippet is exactly what is needed.
if (!context.Request.IsHttps)
{
if (context.Request.Headers.ContainsKey("X-Forwarded-Proto")
&& context.Request.Headers["X-Forwarded-Proto"].Equals("https"))
{
IHttpRequestFeature httpRequestFeature = context.Features.Get<IHttpRequestFeature>();
httpRequestFeature.Scheme = "https";
}
}
This snippet can be easily wrapped inside a reusable and parametrized middleware like one here.
This scenario is a nice example of how ASP.NET Core is layered and how much power gives the access to the low level building blocks.