【问题标题】:Preventing IDOR Attacks Using Aspect Oriented Programming使用面向方面编程防止 IDOR 攻击
【发布时间】:2020-12-13 12:10:58
【问题描述】:

我正在寻找一种方法来防止不安全的直接对象引用 (IDOR) 攻击,使用 AOP,因此我不需要向每个 API 控制器方法添加代码来检查它。

IDOR 攻击的一个示例是拦截请求并更改请求的正文,以便对象的 CompanyId 和其他属性不同,以尝试将记录插入到不同的公司(除了用户所属的公司——在 JWT 中指定)。

问题是每个方法都接受自己的参数作为不同的类。例如:

public async Task<ActionResult> CreateAdjustment(Adjustment adjustment)

public async Task<ActionResult> CreateHistory(History history)

所以通用代码需要知道如何检查adjustment 对象和history 对象。每个参数都有一个CompanyId 属性。第二种方法可以将history 上的CompanyId 属性与JWT 中的CompanyId 进行比较(如果它们不同则抛出异常)。

我只是不想将相同的代码添加到每个 API 控制器方法中,如下所示:

    var companyClaim = HttpContext.User.Claims
        .FirstOrDefault(x => x.Type == Consts.Claims.CompanyId);
    
    if (companyClaim == default) { return Unauthorized(); }
    
    if (Convert.ToInt32(companyClaim.Value) != history.CompanyId)
        { return Unauthorized(); }

也许每个 DTO/域参数都实现了 ICompanyWhatever 之类的东西来表示它将具有 CompanyId 属性?那么AOP代码可以用同样的方式检查每个参数吗?

我遇到的问题是属性无法访问方法的参数。还是他们?有没有办法从属性中得到它?如果没有,也许使用拦截?

【问题讨论】:

  • 我现在正在研究 IActionFilter。它在模型绑定之后执行,但在执行操作方法之前。这可能是我需要的。

标签: c# .net-core attributes aop


【解决方案1】:

我想我会发布一个工作示例。我确定我会对此进行调整以使其更好,但它回答了我上面的问题。我也可以将它移动为一个动作属性,但我们会看到。

基本上,我只是从声明中获取公司 ID,然后将其与 API 控制器的参数进行比较。如果参数上有 CompanyId,我们会将其值与声明中的值进行比较。如果它们不匹配,则抛出异常。

using System;
using System.Linq;
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Filters;
using SomeDepartment.SomeProject.Shared.Common;

namespace ReadyTime.TimeOff.Shared.Infrastructure.Filters
{
    public class CompanyValidationActionFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            var companyClaim = context.HttpContext.User.Claims.FirstOrDefault(x => x.Type == Consts.Claims.CompanyId);

            if (companyClaim == default) { throw new UnauthorizedAccessException("Company claim not found."); }

            long claimCompanyId = Convert.ToInt32(companyClaim.Value);

            // Make sure every method parameter object, that has a CompanyId property, matches in value with the
            // company ID in claims.
            foreach (var argument in context.ActionArguments)
            {
                PropertyInfo companyIdProperty = argument.Value.GetType().GetProperty("CompanyId",
                    BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                
                if (companyIdProperty == default) { continue; } // Not an object with a CompanyId property, so ignore.

                long companyIdPropertyAsLong = (long)Convert.ChangeType(companyIdProperty.GetValue(argument.Value),
                    TypeCode.Int64);

                if (companyIdPropertyAsLong != claimCompanyId)
                {
                    throw new UnauthorizedAccessException("The CompanyId value on the parameter does not match claims.");
                }
            }
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // do nothing
        }        
    }
}

【讨论】:

    【解决方案2】:

    关于 AOP 的另一种选择: 您只需要拦截器控制器进行过滤,请参阅下面的控制台演示

    // 1
    public class ConsoleInterceptor : AbstractInterceptor
     {
         public override async Task InvokeAsync(AspectContext context, AsyncAspectDelegate next)
         {
             Console.WriteLine($"{context.Service.GetType().GetReflector().FullDisplayName}.{context.Method.GetReflector().DisplayName}");
             await next(context);
         }
     }
    
     // 2
     [ApiController]
     [Route("[controller]")]
     public class WeatherForecastController : ControllerBase
     {
         [HttpGet]
         public virtual IEnumerable<WeatherForecast> Get() => test.Get();
     }
    
     // 3
     public void ConfigureServices(IServiceCollection services)
     {
         services.AddControllers().AddControllersAsServices();
         services.ConfigureAop(i => i.GlobalInterceptors.Add(new ConsoleInterceptor()));
     }
     
    

    您将在控制台中看到以下输出: Norns.Urd.DynamicProxy.Generated.WeatherForecastController_Proxy_Inherit.IEnumerable&lt;WeatherForecast&gt; Get()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-10-10
      • 2018-07-16
      • 1970-01-01
      • 1970-01-01
      • 2022-01-08
      • 1970-01-01
      相关资源
      最近更新 更多