如何為ASP.NET Core設置客戶端IP白名單驗證


原文鏈接:Client IP safelist for ASP.NET Core
作者:Damien Bowden and Tom Dykstra
譯者:Lamond Lu

本篇博文中展示了如何在ASP.NET Core應用程序中設置IP白名單驗證的3種方式。

你可以使用一下3種方式:

  • 使用中間件檢查每個請求的遠程IP地址
  • 使用Action過濾器為指定的Controller或action方法添加針對遠程IP地址的檢查
  • 使用IPageFilter為Razor Pages應用添加針對遠程IP地址的檢查

查看項目源代碼

白名單

這里為了簡化代碼,我們將IP白名單列表放置在配置文件appSettings.json中,每個IP之間使用分號分隔。

正式項目中,可以將這個列表保存在數據庫中,便於管理

{
  "AdminSafeList": "127.0.0.1;192.168.1.5;::1",
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Debug",
      "System": "Information",
      "Microsoft": "Information"
    }
  }
}

使用中間件檢查每個請求的遠程IP地址

這里我們首先添加一個中間件AdminSafeListMiddleware

public class AdminSafeListMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<AdminSafeListMiddleware> _logger;
    private readonly string _adminSafeList;

    public AdminSafeListMiddleware(
        RequestDelegate next, 
        ILogger<AdminSafeListMiddleware> logger, 
        string adminSafeList)
    {
        _adminSafeList = adminSafeList;
        _next = next;
        _logger = logger;
    }

    public async Task Invoke(HttpContext context)
    {
        if (context.Request.Method != "GET")
        {
            var remoteIp = context.Connection.RemoteIpAddress;
            _logger.LogDebug($"Request from Remote IP address: {remoteIp}");

            string[] ip = _adminSafeList.Split(';');

            var bytes = remoteIp.GetAddressBytes();
            var badIp = true;
            foreach (var address in ip)
            {
                var testIp = IPAddress.Parse(address);
                if(testIp.GetAddressBytes().SequenceEqual(bytes))
                {
                    badIp = false;
                    break;
                }
            }

            if(badIp)
            {
                _logger.LogInformation(
                    $"Forbidden Request from Remote IP address: {remoteIp}");
                context.Response.StatusCode = (int)HttpStatusCode.Forbidden;
                return;
            }
        }

        await _next.Invoke(context);

    }
}

代碼解釋:

  • 這里在AdminSafeListMiddleware的構造函數中,我們傳入了從配置文件中讀取的IP白名單列表
  • 當請求進入當前中間件時,我們使用當前請求上下文的context.Connection.RemoteIpAddress獲取到了客戶端的IP
  • 如果客戶端IP存在於IP白名單列表中,就運行下一個中間件,否則就直接返回401狀態碼。
  • 這里源代碼中,只過濾了非GET請求,如果針對GET請求也需要啟動IP白名單,可以去掉這個判斷。

然后我們需要在Startup.cs文件的Configure方法中將中間件添加到ASP.NET Core的中間件管道中。

public void Configure(
    IApplicationBuilder app, 
    IHostingEnvironment env, 
    ILoggerFactory loggerFactory)
{
    loggerFactory.AddNLog();

    app.UseStaticFiles();

    app.UseMiddleware<AdminSafeListMiddleware>(
        Configuration["AdminSafeList"]);
    app.UseMvc();
}

注意: 這里我們在注冊中間件的時候,傳入了從配置文件中讀取的IP白名單。

使用Action過濾器

如果你只是希望為某些特性的Controller或Action方法添加IP白名單,你可以使用Action過濾器。

這里我們首先添加一個新類ClientIdCheckFilter, 它繼承自ActionFilterAttribute

 public class ClientIdCheckFilter : ActionFilterAttribute
    {
        private readonly ILogger _logger;
        private readonly string _safelist;

        public ClientIdCheckFilter
            (ILoggerFactory loggerFactory, IConfiguration configuration)
        {
            _logger = loggerFactory.CreateLogger("ClientIdCheckFilter");
            _safelist = configuration["AdminSafeList"];
        }

        public override void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation(
                $"Remote IpAddress: {context.HttpContext.Connection.RemoteIpAddress}");

            var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
            _logger.LogDebug($"Request from Remote IP address: {remoteIp}");

            string[] ip = _safelist.Split(';');

            var bytes = remoteIp.GetAddressBytes();
            var badIp = true;
            foreach (var address in ip)
            {
                var testIp = IPAddress.Parse(address);
                if (testIp.GetAddressBytes().SequenceEqual(bytes))
                {
                    badIp = false;
                    break;
                }
            }

            if (badIp)
            {
                _logger.LogInformation(
                    $"Forbidden Request from Remote IP address: {remoteIp}");
                context.Result = new StatusCodeResult(401);
                return;
            }

            base.OnActionExecuting(context);
        }
    }

這里代碼邏輯和前面中間的基本一樣,主要的區別是

  • 這里我們是從IP白名單,我們是從IConfiguration對象中手動獲取的
  • 這里我們復寫了OnActionExecuting方法,如果當前客戶端 IP存在於白名單中,我們就調用基類OnActionExecuting方法,執行當前Action請求,否則就返回一個401狀態碼
  • 這里沒有針對請求類型的判斷,所以指定當前過濾器的Action,GET請求也會受到白名單的限制

第二步,我們需要將這action過濾器添加到服務容器中。

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<ClientIdCheckFilter>();

    services.AddMvc(options =>
    {
        options.Filters.Add
            (new ClientIdCheckPageFilter
                (_loggerFactory, Configuration));
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

第三步,我們可以在Action方法聲明處添加ServiceFilter特性,傳入的參數是我們之前定義好的ClientIdCheckFilter

例:

[ServiceFilter(typeof(ClientIdCheckFilter))]
[HttpGet]
public IEnumerable<string> Get()

使用IPageFilter

Razor Pages應用是ASP.NET Core 2.0中新引入的功能,它是ASP.NET Core Mvc的一個子集。

如果希望Razor Pages應用支持IP白名單,我們需要創建一個新類ClientIdCheckPageFilter, 它實現了IPageFilter接口.

public class ClientIdCheckPageFilter : IPageFilter
    {
        private readonly ILogger _logger;
        private readonly string _safelist;

        public ClientIdCheckPageFilter
            (ILoggerFactory loggerFactory, IConfiguration configuration)
        {
            _logger = loggerFactory.CreateLogger("ClientIdCheckPageFilter");
            _safelist = configuration["AdminSafeList"];
        }

        public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
        {
            _logger.LogInformation(
                $"Remote IpAddress: {context.HttpContext.Connection.RemoteIpAddress}");

            var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
            _logger.LogDebug($"Request from Remote IP address: {remoteIp}");

            string[] ip = _safelist.Split(';');

            var bytes = remoteIp.GetAddressBytes();
            var badIp = true;
            foreach (var address in ip)
            {
                var testIp = IPAddress.Parse(address);
                if (testIp.GetAddressBytes().SequenceEqual(bytes))
                {
                    badIp = false;
                    break;
                }
            }

            if (badIp)
            {
                _logger.LogInformation(
                    $"Forbidden Request from Remote IP address: {remoteIp}");
                context.Result = new StatusCodeResult(401);
                return;
            }
        }

        public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
        {
        }

        public void OnPageHandlerSelected(PageHandlerSelectedContext context)
        {
        }
    }

這里的代碼實現和IActionFilter的實現基本一樣,唯一的區別是代碼放在了OnPageHandlerExecuting的實現中。

第二步,我們還是需要將ClientIdCheckPageFilter添加到MVC的過濾器集合中。

public void ConfigureServices(IServiceCollection services)
{
    services.AddScoped<ClientIdCheckFilter>();

    services.AddMvc(options =>
    {
        options.Filters.Add
            (new ClientIdCheckPageFilter
                (_loggerFactory, Configuration));
    }).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

總結

本篇我們講解了在ASP.NET Core中啟用IP白名單驗證的3種方式

  • 使用中間件檢查每個請求的遠程IP地址
  • 使用Action過濾器為指定的Controller或action方法添加針對遠程IP地址的檢查
  • 使用IPageFilter為Razor Pages應用添加針對遠程IP地址的檢查


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM