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

如何限制ASP.NET Web API被篡改请求参数进行非法查询

解决方案建议

以下是几种针对.NET API防止请求参数篡改、实现细粒度资源访问控制的方案:

1. 基于声明的资源授权策略

在用户的Bearer Token中嵌入其有权访问的资源标识(如资源ID/GUID列表),通过.NET的授权策略验证请求参数是否在用户的权限范围内:

  • 定义自定义授权需求:
    public class ResourceAccessRequirement : IAuthorizationRequirement
    {
        public string ResourceIdClaimType { get; }
        public ResourceAccessRequirement(string claimType) => ResourceIdClaimType = claimType;
    }
    
  • 实现授权处理器:
    public class ResourceAccessHandler : AuthorizationHandler<ResourceAccessRequirement>
    {
        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ResourceAccessRequirement requirement)
        {
            // 从请求路由中获取资源ID
            var routeData = context.Resource as HttpContext;
            var resourceId = routeData?.Request.RouteValues["id"]?.ToString();
            
            // 从用户Claims中提取允许访问的资源列表
            var allowedResources = context.User.FindAll(requirement.ResourceIdClaimType).Select(c => c.Value);
            
            if (!string.IsNullOrEmpty(resourceId) && allowedResources.Contains(resourceId))
            {
                context.Succeed(requirement);
            }
            return Task.CompletedTask;
        }
    }
    
  • 注册策略并在API上启用:
    services.AddAuthorization(options =>
    {
        options.AddPolicy("ResourceAccess", policy =>
            policy.Requirements.Add(new ResourceAccessRequirement("AllowedResourceIds")));
    });
    services.AddSingleton<IAuthorizationHandler, ResourceAccessHandler>();
    
    控制器方法上标注:[Authorize(Policy = "ResourceAccess")]

2. 请求参数签名验证

前端对关键请求参数(资源ID、时间戳等)生成专属签名,后端重新计算签名并比对,不一致则拒绝请求:

  • 前端示例(CryptoJS实现):
    const secretKey = 'your-shared-secret'; // 建议从后端动态获取临时密钥
    const resourceId = 'target-resource-guid';
    const timestamp = Date.now().toString();
    const signature = CryptoJS.HmacSHA256(`${resourceId}:${timestamp}`, secretKey).toString();
    
    // 请求时携带签名与时间戳
    fetch(`/api/resources/${resourceId}`, {
        headers: {
            'Authorization': `Bearer ${token}`,
            'X-Signature': signature,
            'X-Timestamp': timestamp
        }
    });
    
  • 后端验证逻辑:
    [HttpGet("{id}")]
    public IActionResult GetResource(string id)
    {
        var signature = Request.Headers["X-Signature"].FirstOrDefault();
        var timestamp = Request.Headers["X-Timestamp"].FirstOrDefault();
        var secretKey = "your-shared-secret"; // 与前端一致的密钥
        
        // 验证时间戳有效性(防止重放攻击)
        if (Math.Abs(long.Parse(timestamp) - DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()) > 300000) // 5分钟有效期
        {
            return Unauthorized();
        }
        
        // 重新计算签名并比对
        var computedSignature = Convert.ToBase64String(new HMACSHA256(Encoding.UTF8.GetBytes(secretKey))
            .ComputeHash(Encoding.UTF8.GetBytes($"{id}:{timestamp}")));
        
        if (signature != computedSignature)
        {
            return Forbid();
        }
        
        // 后续业务逻辑
        return Ok(yourResource);
    }
    

3. 资源专属临时令牌

当用户打开第三方页面时,后端生成仅能访问该特定资源的临时令牌(有效期短),页面使用此令牌调用API:

  • 生成临时令牌:
    [HttpGet("generate-resource-token/{resourceId}")]
    public IActionResult GenerateResourceToken(string resourceId)
    {
        // 先验证用户基础权限
        if (!UserHasAccessToResource(User, resourceId))
        {
            return Forbid();
        }
        
        // 生成仅包含目标资源ID的JWT令牌,有效期15分钟
        var tokenHandler = new JwtSecurityTokenHandler();
        var key = Encoding.ASCII.GetBytes("resource-token-secret");
        var tokenDescriptor = new SecurityTokenDescriptor
        {
            Subject = new ClaimsIdentity(new Claim[]
            {
                new Claim("ResourceId", resourceId)
            }),
            Expires = DateTime.UtcNow.AddMinutes(15),
            SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
        };
        var token = tokenHandler.CreateToken(tokenDescriptor);
        return Ok(new { ResourceToken = tokenHandler.WriteToken(token) });
    }
    
  • API验证临时令牌:
    [HttpGet("{id}")]
    [Authorize(AuthenticationSchemes = "ResourceTokenScheme")]
    public IActionResult GetResource(string id)
    {
        var resourceIdFromToken = User.FindFirst("ResourceId")?.Value;
        if (resourceIdFromToken != id)
        {
            return Forbid();
        }
        return Ok(yourResource);
    }
    
    需在Startup中注册对应的认证方案。

4. 自定义授权属性

编写自定义属性,在授权阶段直接验证请求参数与用户权限的匹配:

public class ResourceAuthorizeAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;
        if (!user.Identity.IsAuthenticated)
        {
            context.Result = new UnauthorizedResult();
            return;
        }
        
        // 从路由获取资源ID
        var resourceId = context.RouteData.Values["id"]?.ToString();
        if (string.IsNullOrEmpty(resourceId))
        {
            context.Result = new BadRequestResult();
            return;
        }
        
        // 验证用户是否有权访问该资源
        if (!UserHasAccessToResource(user, resourceId))
        {
            context.Result = new ForbidResult();
        }
    }
    
    private bool UserHasAccessToResource(ClaimsPrincipal user, string resourceId)
    {
        // 替换为实际权限校验逻辑,比如从数据库查询用户权限
        var allowedResources = user.FindAll("AllowedResourceIds").Select(c => c.Value);
        return allowedResources.Contains(resourceId);
    }
}

使用时直接标注在控制器方法上:[ResourceAuthorize]

5. 数据层自动过滤

在数据访问层(如EF Core)添加全局查询过滤器,自动根据当前用户身份过滤数据,从根源上防止非法访问:

public class YourDbContext : DbContext
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    
    public YourDbContext(DbContextOptions<YourDbContext> options, IHttpContextAccessor httpContextAccessor)
        : base(options)
    {
        _httpContextAccessor = httpContextAccessor;
    }
    
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Resource>()
            .HasQueryFilter(r => 
                r.OwnerId == _httpContextAccessor.HttpContext.User.FindFirstValue("UserId")
                || _httpContextAccessor.HttpContext.User.IsInRole("Admin"));
    }
}

这种方式下,即使请求参数被篡改,数据层也只会返回用户有权访问的资源。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 13:53:11