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

在Asp.net MVC与Knockout.js应用中解决IDOR漏洞的轻量方案咨询

针对Asp.net MVC + Knockout.js应用中CustomerKey IDOR漏洞的低侵入修复方案

核心问题分析

当前漏洞的根源在于:

  1. 明文CustomerKey(客户ID)直接暴露在前端隐藏字段,可被攻击者获取并暴力枚举
  2. 匿名接口UpLoadLog直接接受任意CustomerKey,无验证逻辑
  3. 授权接口Customer允许传入CustomerKey覆盖会话中的值,存在会话劫持风险

以下是无需大规模改动业务代码的修复方案,按侵入性从低到高排序:


方案一:直接修复授权接口的参数覆盖问题(最小改动)

针对Customer接口,禁止外部传入CustomerKey覆盖会话值,强制从OwinContext读取已验证的用户身份:

[AuthorizeUser]
// 移除CustomerKey参数,避免外部传入覆盖会话值
public ActionResult Customer(string FeedData, string FeedInfo)
{
    var owinContext = Request.GetOwinContext();
    // 强制从会话获取已验证的CustomerKey
    var customerKey = owinContext.Get<string>("CustomerKey");
    // 仅当会话中无值时,才使用传入的FeedData/FeedInfo(可选)
    FeedData = owinContext.Get<string>("FeedData") ?? FeedData;
    FeedInfo = owinContext.Get<string>("FeedInfo") ?? FeedInfo;

    ViewBag.CustomerKey = customerKey;
    ViewBag.FeedData = FeedData;
    ViewBag.FeedInfo = FeedInfo;
    ViewBag.UseSignalR = true;
    ViewBag.isOffline = true;
    return View("Offline", "CustomerLayout");
}

优势:改动极小,直接切断攻击者通过授权接口劫持会话的路径。


方案二:给匿名接口添加会话验证(针对登录用户场景)

如果UpLoadLog仅用于登录用户上传自身日志,直接移除AllowAnonymous特性,并验证传入的CustomerKey与会话中一致:

[HttpPost]
// 移除AllowAnonymous,仅允许已登录用户访问
public JsonResult UpLoadLog(string CustomerKey, string message)
{
    try
    {
        var owinContext = Request.GetOwinContext();
        var sessionCustomerKey = owinContext.Get<string>("CustomerKey");
        
        // 验证传入的CustomerKey是否与会话一致
        if (!string.Equals(CustomerKey, sessionCustomerKey, StringComparison.Ordinal))
        {
            HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.Forbidden;
            return Json(new { success = false, message = "无效的CustomerKey" });
        }

        var log = message.Split(new string[] { "\r\n" }, 
        StringSplitOptions.RemoveEmptyEntries);
        foreach (string s in log)
        {
            Logger.Write(LogType.Info, this.GetType(), "UpLoadLog", CustomerKey + ": " 
            + s.TrimStart('\r', '\n'), null);
        }
    }
    catch (Exception ex)
    {
        Logger.Write(LogType.Fatal, this.GetType(), "UpLoadLog", ex.Message, ex);
        HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
        return Json(new { success = false, message = ex.Message });
    }
    HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
    return Json(new { success = true });
}

优势:仅修改接口权限和验证逻辑,不影响现有业务流程。


方案三:引入会话绑定的随机令牌(最彻底,适配多用户SignalR)

不加密CustomerKey本身,而是生成短期有效、与会话绑定的随机令牌,前端仅存储令牌,后端通过映射表获取真实CustomerKey:

1. 登录逻辑修改(生成令牌)

// 登录成功后,获取真实CustomerKey
var realCustomerKey = "从数据库获取的客户ID";
// 生成随机令牌
var customerToken = Guid.NewGuid().ToString();
// 将令牌与真实CustomerKey存入缓存(Redis/内存缓存,设置1小时过期)
RedisCache.Set(customerToken, realCustomerKey, TimeSpan.FromHours(1));
// 令牌存入会话,供业务逻辑使用
var owinContext = Request.GetOwinContext();
owinContext.Set("CustomerToken", customerToken);
owinContext.Set("CustomerKey", realCustomerKey);
// 前端隐藏字段改为存储令牌
ViewBag.CustomerToken = customerToken;

2. 前端代码修改

<!-- 替换原隐藏字段 -->
<input type="hidden" id="customerToken" value="@ViewBag.CustomerToken"/>
var UpLoadLog = function (log, isAsync) {
    if (isAsync == null || isAsync == undefined)
        isAsync = true;   
    jQuery.ajax({
        type: "POST",
        async: isAsync,
        contentType: "application/json;charset=utf-8",
        url: rooturl + "Authentication/UpLoadLog",
        // 传入令牌而非真实CustomerKey
        data: JSON.stringify({ CustomerToken: jQuery("#customerToken").val(), message: "\r\n\r\n" + log + "\r\n\r\n" }),
        dataType: "json",
        success: function (response, status, jqXHR) {           
        },
        error: function (jqXHR, status, error) {

        }
    });

    LogMessages = "\r\n\r\n";
};

3. 后端接口修改(令牌转真实CustomerKey)

[HttpPost]
public JsonResult UpLoadLog(string CustomerToken, string message)
{
    try
    {
        // 从缓存获取真实CustomerKey
        var customerKey = RedisCache.Get<string>(CustomerToken);
        if (string.IsNullOrEmpty(customerKey))
        {
            HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.Unauthorized;
            return Json(new { success = false, message = "令牌无效或已过期" });
        }

        var log = message.Split(new string[] { "\r\n" }, 
        StringSplitOptions.RemoveEmptyEntries);
        foreach (string s in log)
        {
            Logger.Write(LogType.Info, this.GetType(), "UpLoadLog", customerKey + ": " 
            + s.TrimStart('\r', '\n'), null);
        }
    }
    catch (Exception ex)
    {
        Logger.Write(LogType.Fatal, this.GetType(), "UpLoadLog", ex.Message, ex);
        HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.InternalServerError;
        return Json(new { success = false, message = ex.Message });
    }
    HttpContext.Response.StatusCode = (int)System.Net.HttpStatusCode.OK;
    return Json(new { success = true });
}

4. SignalR适配(令牌验证)

public class CustomerHub : Hub
{
    public override Task OnConnected()
    {
        var customerToken = Context.QueryString["customerToken"];
        var customerKey = RedisCache.Get<string>(customerToken);
        if (string.IsNullOrEmpty(customerKey))
        {
            Context.Abort(); // 拒绝无效令牌的连接
            return base.OnConnected();
        }
        // 将连接与客户分组,不影响多用户同时登录
        Groups.Add(Context.ConnectionId, customerKey);
        return base.OnConnected();
    }
}

优势:完全隐藏真实CustomerKey,暴力破解难度极大,适配多用户SignalR场景,业务代码无需大规模修改。


方案四:添加限流(辅助防暴力枚举)

配合上述方案,给UpLoadLog接口添加请求限流,防止攻击者暴力枚举令牌或CustomerKey:

// 在Program.cs/Startup.cs中配置限流
builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("UploadLogLimit", opt =>
    {
        opt.Window = TimeSpan.FromMinutes(1);
        opt.PermitLimit = 5; // 每分钟最多5次请求
        opt.QueueLimit = 0;
    });
});

// 接口添加限流特性
[HttpPost]
[EnableRateLimiting("UploadLogLimit")]
public JsonResult UpLoadLog(...)
{
    // 接口逻辑
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 00:15:29