You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ASP.NET Core Web API中IDOR漏洞修复方案咨询

修复IDOR漏洞(Angular 16 + .NET Core)及ID加密方案

核心认知:IDOR的本质是权限校验缺失

加密ID只是辅助防护手段,不能替代后端的权限验证——哪怕ID藏得再好,只要接口没有校验用户对目标对象的访问权限,攻击者依然可以通过其他手段(比如遍历加密后的ID)获取敏感数据。修复IDOR的核心是确保每个请求都经过严格的权限校验。


具体修复方案

1. 后端(.NET Core):强制权限校验(最关键)

所有涉及对象访问的接口(如获取、修改、删除资源)必须添加权限校验逻辑:

  • 对象归属校验:比如用户请求 /api/orders/{id} 时,后端需查询该订单的OwnerId,与当前登录用户的ID(从JWT Claims或会话中获取)比对,只有匹配或用户拥有管理员权限时才允许访问。
  • 统一校验逻辑:用自定义AuthorizationHandler或ActionFilter实现全局校验,避免重复代码。示例代码:
    // 自定义权限处理器
    public class ObjectOwnershipHandler : AuthorizationHandler<ObjectOwnershipRequirement>
    {
        private readonly AppDbContext _dbContext;
    
        public ObjectOwnershipHandler(AppDbContext dbContext)
        {
            _dbContext = dbContext;
        }
    
        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, ObjectOwnershipRequirement requirement)
        {
            // 从路由获取资源ID
            if (!context.Resource is HttpContext httpContext)
            {
                context.Fail();
                return;
            }
            if (!httpContext.Request.RouteValues.TryGetValue("id", out var idValue) || !Guid.TryParse(idValue.ToString(), out var resourceId))
            {
                context.Fail();
                return;
            }
    
            // 获取当前用户ID
            var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier);
            if (string.IsNullOrEmpty(userId))
            {
                context.Fail();
                return;
            }
    
            // 根据资源类型校验归属(这里以订单为例)
            var order = await _dbContext.Orders.FindAsync(resourceId);
            if (order?.OwnerId == userId || context.User.IsInRole("Admin"))
            {
                context.Succeed(requirement);
            }
            else
            {
                context.Fail();
            }
        }
    }
    
    // 注册到DI
    services.AddAuthorization(options =>
    {
        options.AddPolicy("ObjectOwnership", policy =>
            policy.Requirements.Add(new ObjectOwnershipRequirement()));
    });
    services.AddScoped<IAuthorizationHandler, ObjectOwnershipHandler>();
    
  • 避免直接暴露数据库ID:后端返回给前端的资源标识,优先使用加密后的ID或映射ID,而非原始数据库自增ID。

2. 前端(Angular 16):减少暴露风险(辅助)

前端的防护只能降低暴露概率,不能依赖它做安全屏障:

  • 不在页面源码、控制台日志、localStorage/sessionStorage中存储原始数据库ID。
  • 路由跳转或API请求时,仅使用后端返回的加密/映射ID,不手动拼接原始ID。
  • 用Angular的路由守卫做基础校验(比如未登录用户无法进入敏感页面),但这只是前端层面的拦截,不能替代后端校验。

ID加密的最佳算法与实现建议

如果选择用加密ID隐藏原始标识,推荐以下方案:

首选:AES-GCM 对称加密

AES-GCM是认证加密算法,同时提供保密性和完整性(防止加密后的ID被篡改),适合ID加密场景:

  • .NET Core 实现:使用内置的AesGcm类,注意每次加密生成随机12字节的nonce(不要重复使用同一nonce和密钥),加密结果包含nonce、密文和标签(用于校验完整性):
    public static string EncryptId(Guid id, byte[] key)
    {
        using var aesGcm = new AesGcm(key);
        var plaintext = id.ToByteArray();
        var nonce = RandomNumberGenerator.GetBytes(12); // AES-GCM标准nonce长度
        var ciphertext = new byte[plaintext.Length];
        var tag = new byte[16];
    
        aesGcm.Encrypt(nonce, plaintext, ciphertext, tag);
    
        // 将nonce、密文、标签拼接后转Base64(URL安全格式)
        var combined = nonce.Concat(ciphertext).Concat(tag).ToArray();
        return Convert.ToBase64String(combined).Replace('+', '-').Replace('/', '_').TrimEnd('=');
    }
    
    public static Guid? DecryptId(string encryptedId, byte[] key)
    {
        try
        {
            // 还原URL安全的Base64
            var base64 = encryptedId.Replace('-', '+').Replace('_', '/');
            while (base64.Length % 4 != 0)
                base64 += '=';
            var combined = Convert.FromBase64String(base64);
    
            var nonce = combined.Take(12).ToArray();
            var ciphertext = combined.Skip(12).Take(16).ToArray(); // Guid是16字节
            var tag = combined.Skip(28).ToArray();
    
            using var aesGcm = new AesGcm(key);
            var plaintext = new byte[16];
            aesGcm.Decrypt(nonce, ciphertext, tag, plaintext);
    
            return new Guid(plaintext);
        }
        catch
        {
            return null; // 解密失败返回null,拒绝请求
        }
    }
    
  • 密钥管理:密钥绝对不能硬编码在代码中,.NET Core可使用Azure Key Vault、本地Secret Manager或环境变量存储;前端不要持有密钥,加密解密全程在后端完成——前端仅传递加密后的ID字符串。

替代方案:UUID映射表

如果不想用加密,可以在后端维护一张ResourceIdMapping表,存储原始数据库ID与UUID的映射关系:

  • 后端返回资源时,将原始ID替换为对应的UUID;
  • 前端用UUID请求,后端查询映射表得到原始ID,再做权限校验;
  • 优点:无需加密逻辑,实现简单;缺点:需要额外存储,且映射表需定期清理无效条目。

关键注意事项

  1. 加密≠授权:哪怕ID加密了,后端依然要校验用户对目标对象的访问权限,加密只是防止ID被遍历猜测。
  2. 禁用Base64“伪装”:Base64是编码而非加密,任何人都可以轻松解码,不能作为ID防护手段。
  3. 密钥轮换:定期轮换加密密钥,避免密钥泄露导致所有加密ID失效。

内容的提问来源于stack exchange,提问作者Rafael

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 13:13:23