iT邦幫忙

2022 iThome 鐵人賽

DAY 20
1
Software Development

ASP.NET Core 30 天旅程系列 第 20

[Day20]-重新導向攻擊 & IP 安全清單

  • 分享至 

  • xImage
  •  

重新導向攻擊

重新導向至透過要求指定的 URL 的 Web 應用程式,例如 querystring 或表單資料,可能會遭到竄改,以將使用者重新導向至外部惡意 URL。 這項竄改稱為開放式重新導向攻擊。

每當應用程式邏輯重新導向至指定的 URL 時,您必須確認重新導向 URL 尚未遭到竄改。 ASP.NET Core具有內建功能,可協助保護應用程式免于開啟重新導向 (也稱為開放式重新導向) 攻擊。

LocalRedirect

LocalRedirect 如果指定了非本機 URL,將會擲回例外狀況。 否則,其行為就像 Redirect 一樣。

public IActionResult SomeAction(string redirectUrl)
{
    return LocalRedirect(redirectUrl);
}

IsLocalUrl

重新導向之前檢查 URL 是否為本機。

private IActionResult RedirectToLocal(string returnUrl)
{
    if (Url.IsLocalUrl(returnUrl))
    {
        return Redirect(returnUrl);
    }
    else
    {
        return RedirectToAction(nameof(HomeController.Index), "Home");
    }
}

IP 安全清單

可以藉由以下方法來設定允許存取的 IP 清單:
中介軟體
‵ActionFilter
Razor 頁面 Filter

appsetting.json 定義允許 IP

逗點分隔

{
  "AllowList": "127.0.0.1;192.168.1.5;::1"
}

中介軟體

自訂 AllowListMiddleware

app.UseMiddleware<AllowListMiddleware>(Configuration["AllowList"]);
public class AllowListMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<AllowListMiddleware> _logger;
    private readonly byte[][] _safelist;

    public AllowListMiddleware(
        RequestDelegate next,
        ILogger<AllowListMiddleware> 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(
                    "禁止這個 IP : {RemoteIp}", remoteIp);
                context.Response.StatusCode = (int) HttpStatusCode.Forbidden;
                return;
            }
        }

        await _next.Invoke(context);
    }
}

ActionFilter

如果您想要針對特定 MVC Controller 或 Action 限制 IP,請使用 ActionFilter。
ActionFilterAttribute

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

Program.cs

services.AddScoped<ClientIpCheckActionFilter>(container =>
{
    var loggerFactory = container.GetRequiredService<ILoggerFactory>();
    var logger = loggerFactory.CreateLogger<ClientIpCheckActionFilter>();

    return new ClientIpCheckActionFilter(
        Configuration["AllowList"], logger);
});

可以使用 [ServiceFilter] 屬性,將動作篩選套用至 Controller 或 Action

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

Razor 頁面 Filter

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)
    {
    }
}

藉 Razor 由將 Pages 篩選新增至 MVC 篩選集合來啟用 Pages 篩選。
Program.cs

services.AddRazorPages()
    .AddMvcOptions(options =>
    {
        var logger = LoggerFactory.Create(builder => builder.AddConsole())
                        .CreateLogger<ClientIpCheckPageFilter>();
        var filter = new ClientIpCheckPageFilter(
            Configuration["AllowList"], logger);
        
        options.Filters.Add(filter);
    });

參考資料

Client IP safelist for ASP.NET Core
Preventing Open Redirection Attacks (C#)


上一篇
[Day19]- Antiforgery Token 的 Filters
下一篇
[Day21]- API 系列之 RESTful API 是什麼?
系列文
ASP.NET Core 30 天旅程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言