原文鏈接: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地址的檢查