HTTPS is mandatory to grant security to your web application, regardless of the programming framework you are using. But what happens if a client calls your web app with HTTP instead of HTTPS? How can you force it to use HTTPS? Let's delve into the options provided by ASP.NET Core.
Use HTTPS Redirection
I guess that the first idea that comes to your mind is to redirect HTTP requests: if a client calls your application using HTTP, your application redirects it to the same URL starting with HTTPS. URL redirection is a well-known approach. The web application creates an HTTP response with a status code starting with 3
and a Location
header like in the following example:
HTTP/1.1 301 Moved Permanently
Location: https://www.auth0.com/
While this approach doesn't resolve all the security risks, as you will learn along the way, it's a good starting point.
Fortunately, in ASP.NET Core, you don't need to go to the HTTP level to redirect your client's requests. You have a few options to choose from. Let's analyze each of them.
The RequireHttps
attribute
The first approach we'll explore is based on the RequireHttps
attribute. You can use it in your Razor Pages applications to force a page to require HTTPS, as shown in the following code snippet:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
namespace RazorApp.Pages;
[RequireHttps]
public class PrivacyModel : PageModel
{
//...existing code...
}
If this page is called through HTTP, an HTTPS redirection response will be automatically created for you. For Razor Pages, you can apply the RequireHttps
attribute only to classes inheriting from PageModel
. You cannot apply the attribute to the class methods as well.
In ASP.NET Core MVC applications, you can apply the RequireHttps
attribute to classes inherited from Controller
, as in the following example:
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using MvcApp.Models;
namespace MvcApp.Controllers;
[RequireHttps]
public class HomeController : Controller
{
// ...existing code...
}
When the attribute is attached to the controller, the HTTP redirection is applied to any view returned by it. However, in the ASP.NET Core MVC case, you can apply the RequireHttps
attribute to specific views. For example, the following code shows how to require HTTPS redirection only for the Privacy view:
using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using MvcApp.Models;
namespace MvcApp.Controllers;
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
[RequireHttps]
public IActionResult Privacy()
{
return View();
}
}
The redirection approach based on the RequireHttps
attribute is pretty simple. You may also think that the opportunity to apply it selectively to specific pages or views is great because you can limit HTTPS to just pages with confidential content.
Actually, mixing HTTP and HTTPS pages is a really bad idea! Your web application is not secure because it is exposed to HTTPS downgrade attacks. To mitigate this risk, make all your web application's pages accessible only with the HTTPS protocol.
You may think of applying the RequireHttps
attribute to all the pages to reduce the risk, but there are better approaches, as you will see in the next section.
The HTTPS redirection middleware
When you create a web application using one of the standard ASP.NET project templates, the Program.cs
file contains the method invocation highlighted in the following code snippet:
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// ...existing code...
var app = builder.Build();
// ...existing code...
app.UseHttpsRedirection(); //๐ HTTPS redirection
app.UseStaticFiles();
app.UseRouting();
// ...existing code...
The UseHttpsRedirection()
method invocation enables the HTTPS redirection middleware. This means that each request to your application will be inspected and possibly redirected by the middleware. You don't need to look for pages missing the RequireHttps
attribute. All the pages of your application will require HTTPS. If a client requests a page with HTTP, it will be automatically redirected to the corresponding HTTPS-based URL.
Enable the HSTS middleware
The approach based on UseHttpsRedirection()
looks awesome! With just one statement in your Program.cs
file, your entire web application is forced to be called with the HTTPS protocol.
Unfortunately, while this approach is better than having mixed pages, there are still some potential security issues with your application. Forcing a client to switch from HTTP to HTTPS on each request might not be enough to prevent HTTPS downgrade attacks. The attacker could intercept the client's HTTP request before it switches to the corresponding HTTPS request.
You need a way to tell the browser to mandatorily use HTTPS to request any resource of your web application. This way exists: the HTTP Strict-Transport-Security
header (HSTS). Using HSTS, the browser will call your application using HTTP only the very first time. The subsequent requests against the same domain will be made using the HTTPS protocol, even in the presence of a URL using the HTTP scheme. To have more details about HSTS, check out here.
To enable HSTS in your ASP.NET Core application, you just need to invoke the UseHsts()
method in your Program.cs
file as shown below:
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// ...existing code...
var app = builder.Build();
// ...existing code...
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts(); //๐ Enable HSTS middleware
}
// ...existing code...
By invoking the UseHsts()
method, you enable the HSTS middleware. You have this code already in your application when you build it by starting from a standard ASP.NET Core template.
Using HSTS in the development environment
From the code above, you may notice that the HSTS support is enabled only if your application is not running in your development environment. There is a practical reason behind this choice.
Chances are that you use localhost as your development environment domain. If you use HSTS in your development environment, any request made by your browser to localhost will use HTTPS. Actually, using HTTPS in your development environment is a good practice. But suppose your ASP.NET Core application enables HSTS. In that case, it causes your browser to make HTTPS requests to any application hosted on localhost, not just your ASP.NET Core application and not just ASP.NET Core applications in general. This may lead to headaches in case you have, say, an Angular application that doesn't use HTTPS and stops working overnight.
HSTS settings include an expiration time, which by default is 30 days for ASP.NET Core applications. To change this and other settings, check out the official documentation.
Learn web security through a hands-on exploration of some of the most notorious threats.
DOWNLOAD THE FREE EBOOKHTTPS Redirection and APIs
At this point, we can feel happy with the HTTPS security of our ASP.NET Core application, right? Well, not really. Actually, it depends on the type of web application.
The HTTPS redirection approach relies on sending back to the client a 301
or another 30*
HTTP status code, regardless you are using the RequireHttps
attribute or the HTTPS redirection middleware. The HSTS approach relies on sending the Strict-Transport-Security
header.
Both approaches are well-understood by standard browsers. So, application types whose clients are browsers, such as ASP.NET Core MVC applications, Razor Pages applications, and Blazor Server applications, can rely on these approaches.
However, those approaches are usually ignored by non-browser clients, such as API clients. It's extremely rare for a mobile app or a SPA to take care of 301
status codes or HSTS headers. In this case, you have two alternative ways to deal with clients that make HTTP requests:
- Ignore HTTP requests.
- Respond with a
400 Bad Request
status code.
Ignore HTTP requests
The first option can be done in different ways. One of the easiest ways is to use the --urls
flag of the dotnet run
command, as shown below:
dotnet run --urls "https://localhost:7123"
This approach allows you to override the URL settings configured in the Properties/launchSettings.json
file of your ASP.NET Core project.
A more production-oriented approach to override those settings uses the ASPNETCORE_URLS
environment variable. The following shows how to set this variable in PowerShell:
$Env: ASPNETCORE_URLS = "https://localhost:7123"
This is an example in a bash shell:
export ASPNETCORE_URLS="https://localhost:7123"
Check out this article to learn other ways to override the current listening URLs in ASP.NET Core.
Treat HTTP requests as bad requests
To implement the Bad Request approach, you need to create a custom middleware and use it instead of HTTPS redirection and HSTS middleware. The following example shows a simple version of such a middleware:
// Program.cs
var builder = WebApplication.CreateBuilder(args);
// ...existing code...
var app = builder.Build();
// ...existing code...
// ๐ new code
//app.UseHttpsRedirection();
app.Use(async (context, next) => {
if (!context.Request.IsHttps) {
context.Response.StatusCode = StatusCodes.Status400BadRequest;
await context.Response.WriteAsync("HTTPS required!");
} else {
await next(context);
}
});
/ ๐ new code
app.UseStaticFiles();
app.UseRouting();
// ...existing code...
The highlighted code shows that the existing UseHttpsRedirection()
method invocation is replaced by the custom middleware. The middleware's code just checks if the current request uses HTTPS. If this is not the case, it replies with a 400 Bad Request
status code and a message warning that HTTPS is required.
HTTPS Redirection and Reverse Proxies
All the above makes sense if your ASP.NET Core application is directly exposed to the Internet. If your application is deployed in an environment with a reverse proxy that handles connection security, there is no need to use HTTPS redirection or HSTS middleware.
In this case, you can simply remove the UseHttpsRedirection()
and the UseHsts()
method calls from your ASP.NET Core applications. The same applies to ASP.NET Core Web API application as well: you don't need to create a custom middleware to deny HTTP requests. You delegate HTTP to HTTPS switching and control to the reverse proxy.
Summary
This article guided you through the different approaches to force a client to use HTTPS when it calls an ASP.NET Core application. You started from the simple RequireHttps
attribute, which redirects to HTTPS-based requests on a page-by-page basis. You then explored the UseHttpsRedirection()
method, which allows you to apply HTTPS redirection to all your application's pages.
You learned that redirecting from HTTP to HTTPS at each page request doesn't guarantee you are not exposed to HTTPS downgrade risks. You took a further step in mitigating this risk by learning about HSTS and the UseHsts()
method.
You also learned that HTTPS redirection and HSTS are great approaches for regular web applications, such as ASP.NET Core MVC, Razor, and Blazor applications, but they are not suitable for APIs. In this case, you need to ignore HTTP requests or mark them as bad requests.
Finally, you only need to apply all these measures if your deployment environment doesn't take care of protocol switching and control. For example, if your application runs behind a reverse proxy, you can delegate all these checks to it.