如何為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 context.Connection.RemoteIpAddress
然後我們需要在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地址的檢查