Upgrading browsers visiting your website to secure connection is a best practice and it is easy to do. I have decided to share my implementation because I had seen many partial or insecure implementations. Correct implementation satisfies both backward compatibility and security requirements of various web browsers. This article covers what you need to know before you start to redirect your users to HTTPS protocol.
What attackers do
Some naïve web developers use HTTPS connection only when the browser sends a form with sensitive data to the server. Once a wireless network encryption is weak (which is the case of the most public wireless networks or cellular data networks), the attacker can modify server’s response in a way that the HTML form will be sent to attacker’s website. That’s why it is essential to bypass unencrypted connection completely.
Upgrade-Insecure-Requests HTTP header
The browser can send an Upgrade-Insecure-Requests: 1 header in a request. By applying this header, it instructs the server that the client prefers an encrypted connection. Who wants an unencrypted connection? For example, web crawlers can save processor’s time because an optical connection between datacenters is harder to intercept (it is at least much more noticeable). Web crawlers are mostly interested in public data only.
It would be a bad practice to automatically redirect every request to HTTPS. Web API can accept large requests through POST requests. When the POST request is redirected (it now enables RFC7231 which obsoletes RFC2316), the client must transfer whole payload repeatedly. It may be better to return a HTTP client error status code to force the client sending the request straight to the HTTPS endpoint.
Strict-Transport-Security HTTP header
The server can send a Strict-Transport-Security: max-age=<expire-time> in a response. By applying this header, it instructs the browser to automatically convert all attempts to open the site to HTTPS connection. Browsers accept this header only if secure connection is already established because an attacker may remove this header when your site is using plain HTTP.
A website redirection from HTTP to HTTPS isn’t secure because an attacker may replace the location which the browser is redirected to. Strict-Transport-Security (HSTS) header was intended to bypass this opportunity for a man-in-the-middle attack. There is a good chance that the user visits your site from a non-compromised network. When he visits your site afterwards, the browser will always use HTTPS and the attacker will miss an opportunity to man-in-the-middle attack even in a compromised network.
ASP.NET MVC action filter for HTTPS
An action filter is an attribute that you can apply to an action or an entire controller. It modifies the way in which the request is executed. The TlsAttribute redirects users to secure connection when the browser sends the Upgrade-Insecure-Requests header and the request isn’t localhost. If the connection is secure, it adds a HSTS header to the server’s response.
public class TlsAttribute : ActionFilterAttribute {
public override void OnActionExecuting(ActionExecutingContext filterContext) {
var request = filterContext.HttpContext.Request;
if (request.IsSecureConnection) {
filterContext.HttpContext.Response.AddHeader("Strict-Transport-Security", "max-age=15552000");
} else if (!request.IsLocal && request.Headers["Upgrade-Insecure-Requests"] == "1") {
var url = new Uri("https://" + request.Url.GetComponents(UriComponents.Host | UriComponents.PathAndQuery, UriFormat.Unescaped), UriKind.Absolute);
filterContext.Result = new RedirectResult(url.AbsoluteUri);
}
}
}
Then the action filter can be easily applied.
[HttpGet, Tls]
public ActionResult BlogPost(int id) {
...
}