首页 > 解决方案 > How do you restrict requests so they only work if it is accessed by a specific domain

问题描述

I have this ASP.NET Web API and I want to restrict access so it only works when called by specific host. I cannot, for what I know until now, secure it by token, because the WEB API url will be a postback url for a system that will call it automatically when a certain action is made. I have checked out CORS, but what CORS seems to do is to allow a specific domain to access the API. So, does this mean that my WEB API is already restricted for other domains? Then why I can access it by Postman locally, even if it is hosted in Azure?

I just want my service to allow calls from localhost and another specific domain only.

How do I achieve this?

Thanks!

标签: c#asp.net-web-apiasp.net-web-api2

解决方案


一种可能性是使用自定义授权过滤器,该过滤器创建一个带有失败状态代码的 HTTP 响应,如 400 bad request 或 404 not found 如果请求具有不允许的主机。我们可以定义一个如下所示的授权过滤器RestrictDomain

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");
        }
    }
}

如果要RestrictDomain全局应用过滤器,则可以在Startup.cs文件中添加过滤器,如下所示:

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());
    }
}

使用此设置,如果我从构造函数中删除“localhost”并且只允许“example.com”,当我使用 Postman 时会收到 400 请求,因为主机将是 localhost。

另一种选择是直接在控制器或控制器操作中使用过滤器,因为我们将其配置为作为属性工作。但是,允许的主机必须是常量值,而不是可以在运行时计算的值。这是一个例子:

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!");
}

如果您不想要常量值并且不想全局应用过滤器,那么您可以将 注入IConfiguration过滤器以读取可以访问资源的允许主机。


推荐阅读