events
ASP.NET Core のクライアント IP セーフリスト
作成者: Damien Bowden、Tom Dykstra
この記事では、ASP.NET Core アプリに IP アドレスのセーフリスト (許可リストとも呼ばれます) を実装する 3 つの方法について説明します。 付属のサンプル アプリは、3 つの方法すべてを示しています。 使用できるもの:
- ミドルウェアを使用して、すべての要求のリモート IP アドレスを確認します。
- 特定のコントローラーまたはアクション メソッドに対する要求のリモート IP アドレスを確認する MVC アクション フィルター。
- Razor ページに対する要求のリモート IP アドレスを確認する Razor Pages フィルター。
いずれの場合も、承認済みクライアントの IP アドレスを含む文字列はアプリの設定に格納されます。 ミドルウェアまたはフィルター:
- 文字列を配列に解析します。
- リモート IP アドレスが配列に存在するかどうかを確認します。
配列に IP アドレスが含まれている場合はアクセスが許可されます。 それ以外の場合は、HTTP 403 Forbidden 状態コードが返されます。
サンプル コードを表示またはダウンロードします (ダウンロード方法)。
サンプル アプリの IP アドレス セーフリストは次のとおりです。
appsettings.json
ファイルのAdminSafeList
プロパティで定義されています。- インターネット プロトコル バージョン 4 (IPv4) とインターネット プロトコル バージョン 6 (IPv6) の両方のアドレスを含むことができるセミコロン区切りの文字列です。
{
"AdminSafeList": "127.0.0.1;192.168.1.5;::1",
"Logging": {
前の例では、IPv4 アドレスの 127.0.0.1
と 192.168.1.5
、IPv6 ループバック アドレスの ::1
(0:0:0:0:0:0:0:1
の圧縮形式) が許可されています。
Startup.Configure
メソッドを使うと、カスタムの AdminSafeListMiddleware
ミドルウェア型をアプリの要求パイプラインに追加できます。 セーフリストは、.NET Core 構成プロバイダーを使って取得され、コンストラクターのパラメーターとして渡されます。
app.UseMiddleware<AdminSafeListMiddleware>(Configuration["AdminSafeList"]);
ミドルウェアによって文字列は配列に解析され、配列内のリモート IP アドレスが検索されます。 リモート IP アドレスから見つからない場合、ミドルウェアから HTTP 403 Forbidden が返されます。 HTTP GET 要求の場合、この検証プロセスはバイパスされます。
public class AdminSafeListMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger<AdminSafeListMiddleware> _logger;
private readonly byte[][] _safelist;
public AdminSafeListMiddleware(
RequestDelegate next,
ILogger<AdminSafeListMiddleware> logger,
string safelist)
{
var ips = safelist.Split(';');
_safelist = new byte[ips.Length][];
for (var i = 0; i < ips.Length; i++)
{
_safelist[i] = IPAddress.Parse(ips[i]).GetAddressBytes();
}
_next = next;
_logger = logger;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Method != HttpMethod.Get.Method)
{
var remoteIp = context.Connection.RemoteIpAddress;
_logger.LogDebug("Request from Remote IP address: {RemoteIp}", remoteIp);
var bytes = remoteIp.GetAddressBytes();
var badIp = true;
foreach (var address in _safelist)
{
if (address.SequenceEqual(bytes))
{
badIp = false;
break;
}
}
if (badIp)
{
_logger.LogWarning(
"Forbidden Request from Remote IP address: {RemoteIp}", remoteIp);
context.Response.StatusCode = (int) HttpStatusCode.Forbidden;
return;
}
}
await _next.Invoke(context);
}
}
特定の MVC コントローラーまたはアクション メソッドに対してセーフリスト駆動型のアクセス制御が必要な場合は、アクション フィルターを使います。 次に例を示します。
public class ClientIpCheckActionFilter : ActionFilterAttribute
{
private readonly ILogger _logger;
private readonly string _safelist;
public ClientIpCheckActionFilter(string safelist, ILogger logger)
{
_safelist = safelist;
_logger = logger;
}
public override void OnActionExecuting(ActionExecutingContext context)
{
var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
_logger.LogDebug("Remote IpAddress: {RemoteIp}", remoteIp);
var ip = _safelist.Split(';');
var badIp = true;
if (remoteIp.IsIPv4MappedToIPv6)
{
remoteIp = remoteIp.MapToIPv4();
}
foreach (var address in ip)
{
var testIp = IPAddress.Parse(address);
if (testIp.Equals(remoteIp))
{
badIp = false;
break;
}
}
if (badIp)
{
_logger.LogWarning("Forbidden Request from IP: {RemoteIp}", remoteIp);
context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
return;
}
base.OnActionExecuting(context);
}
}
Startup.ConfigureServices
に、MVC フィルター コレクションに対するアクション フィルターを追加します。 次の例では、ClientIpCheckActionFilter
アクション フィルターが追加されています。 コンストラクター パラメーターとして、セーフリストとコンソール ロガー インスタンスが渡されます。
services.AddScoped<ClientIpCheckActionFilter>(container =>
{
var loggerFactory = container.GetRequiredService<ILoggerFactory>();
var logger = loggerFactory.CreateLogger<ClientIpCheckActionFilter>();
return new ClientIpCheckActionFilter(
Configuration["AdminSafeList"], logger);
});
services.AddScoped<ClientIpCheckActionFilter>(_ =>
{
var logger = _loggerFactory.CreateLogger<ClientIpCheckActionFilter>();
return new ClientIpCheckActionFilter(
Configuration["AdminSafeList"], logger);
});
これで、[ServiceFilter] 属性を持つコントローラーまたはアクション メソッドにアクション フィルターを適用できるようになります。
[ServiceFilter(typeof(ClientIpCheckActionFilter))]
[HttpGet]
public IEnumerable<string> Get()
サンプル アプリでは、コントローラーの Get
アクション メソッドにアクション フィルターが適用されています。 次を送信してアプリをテストする場合:
HTTP GET 要求。
[ServiceFilter]
属性によってクライアントの IP アドレスが検証されます。Get
アクション メソッドへのアクセスが許可されると、アクション フィルターとアクション メソッドによって、次のコンソール出力のバリエーションが生成されます。dbug: ClientIpSafelistComponents.Filters.ClientIpCheckActionFilter[0] Remote IpAddress: ::1 dbug: ClientIpAspNetCore.Controllers.ValuesController[0] successful HTTP GET
GET 以外の HTTP 要求の動詞。
AdminSafeListMiddleware
ミドルウェアによってクライアントの IP アドレスが検証されます。
Razor Pages アプリのためにセーフリスト駆動型アクセス制御が必要な場合は、Razor Pages フィルターを使います。 次に例を示します。
public class ClientIpCheckPageFilter : IPageFilter
{
private readonly ILogger _logger;
private readonly IPAddress[] _safelist;
public ClientIpCheckPageFilter(
string safelist,
ILogger logger)
{
var ips = safelist.Split(';');
_safelist = new IPAddress[ips.Length];
for (var i = 0; i < ips.Length; i++)
{
_safelist[i] = IPAddress.Parse(ips[i]);
}
_logger = logger;
}
public void OnPageHandlerExecuting(PageHandlerExecutingContext context)
{
var remoteIp = context.HttpContext.Connection.RemoteIpAddress;
if (remoteIp.IsIPv4MappedToIPv6)
{
remoteIp = remoteIp.MapToIPv4();
}
_logger.LogDebug(
"Remote IpAddress: {RemoteIp}", remoteIp);
var badIp = true;
foreach (var testIp in _safelist)
{
if (testIp.Equals(remoteIp))
{
badIp = false;
break;
}
}
if (badIp)
{
_logger.LogWarning(
"Forbidden Request from Remote IP address: {RemoteIp}", remoteIp);
context.Result = new StatusCodeResult(StatusCodes.Status403Forbidden);
return;
}
}
public void OnPageHandlerExecuted(PageHandlerExecutedContext context)
{
}
public void OnPageHandlerSelected(PageHandlerSelectedContext context)
{
}
}
Startup.ConfigureServices
で、Razor Pages フィルターを MVC フィルター コレクションに追加して有効にします。 次の例では、ClientIpCheckPageFilter
Razor Pages フィルターが追加されています。 コンストラクター パラメーターとして、セーフリストとコンソール ロガー インスタンスが渡されます。
services.AddRazorPages()
.AddMvcOptions(options =>
{
var logger = LoggerFactory.Create(builder => builder.AddConsole())
.CreateLogger<ClientIpCheckPageFilter>();
var filter = new ClientIpCheckPageFilter(
Configuration["AdminSafeList"], logger);
options.Filters.Add(filter);
});
services.AddMvc(options =>
{
var logger = _loggerFactory.CreateLogger<ClientIpCheckPageFilter>();
var clientIpCheckPageFilter = new ClientIpCheckPageFilter(
Configuration["AdminSafeList"], logger);
options.Filters.Add(clientIpCheckPageFilter);
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
サンプル アプリの IndexRazor ページが要求されると、Razor Pages フィルターによってクライアントの IP アドレスが検証されます。 このフィルターによって、次のコンソール出力のバリエーションが生成されます。
dbug: ClientIpSafelistComponents.Filters.ClientIpCheckPageFilter[0]
Remote IpAddress: ::1
ASP.NET Core に関するフィードバック
ASP.NET Core はオープンソース プロジェクトです。 フィードバックを提供するにはリンクを選択します。
その他のリソース
トレーニング
モジュール
ASP.NET Core Blazor Web アプリに HTTP 操作を実装する - Training
ASP.NET Core Blazor Web アプリに HTTP 操作を実装する