One possibility is to use a custom authorization filter that creates an HTTP response with a failure status code like 400 bad request or 404 not found if the requests has a host that is not allowed. We could define an authorization filter named RestrictDomain that looks like this:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
public class RestrictDomainAttribute : Attribute, IAuthorizationFilter
{
public IEnumerable<string> AllowedHosts { get; }
public RestrictDomainAttribute(params string[] allowedHosts) => AllowedHosts = allowedHosts;
public void OnAuthorization(AuthorizationFilterContext context)
{
// Get host from the request and check if it's in the enumeration of allowed hosts
string host = context.HttpContext.Request.Host.Host;
if (!AllowedHosts.Contains(host, StringComparer.OrdinalIgnoreCase))
{
// Request came from an authorized host, return bad request
context.Result = new BadRequestObjectResult("Host is not allowed");
}
}
}
If you want to apply the RestrictDomain filter globally, then you can add the filter in the Startup.cs file like this:
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
// Add the restrict domain filter globally
// You could read the allowed hosts from a config file, here we hard code them
options.Filters.Add(new RestrictDomainAttribute("localhost", "example.com"));
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapControllers());
}
}
With this setting, if I remove "localhost" from the constructor and only allow "example.com", I get a 400 request when I use Postman since the host will be localhost.
Another option is to use the filter in a controller or controller action directly since we configure it to work as an attribute. However, the allowed hosts will have to be constant values instead of values that can be calculated at runtime. Here's an example:
using Microsoft.AspNetCore.Mvc;
[Route("")]
[ApiController]
public class HomeController : ControllerBase
{
[HttpGet]
public ContentResult Index() => Content("Home");
[HttpGet("greeting")]
[RestrictDomain("localhost", "example.com")] // values must be constants
public ContentResult Greeting() => Content("Hello, World!");
}
If you don't want constant values and don't want to apply the filter globally, then you could inject the IConfiguration into the filter to read the allowed hosts that can access the resource.