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

RemoteAuthenticationHandler的state与code_challenge校验问题

问题原因

发起授权挑战(Challenge)的请求和授权服务器回调的请求是两个完全独立的HTTP请求,你在HandleChallengeAsync中声明的state、codeVerifier是方法级局部变量,当前请求结束后就会被内存回收,回调请求到达时自然无法读取。你当前的实现只把nonce持久化到了Cookie中,没有对state、code_verifier做跨请求持久化,所以会出现值丢失的问题。

实现方案

参考你现有nonce的存储逻辑,在发起重定向前将state、code_verifier加密后存入同域Cookie,回调时取出校验即可,具体修改步骤如下:

1. 修改HandleChallengeAsync,持久化跨请求需要的参数

不要明文存储敏感参数,直接用RemoteAuthenticationHandler基类内置的Protector数据保护对象加密后存储,Cookie路径严格绑定回调地址减少泄漏面:

// 原有生成state、nonce、codeVerifier、codeChallenge的逻辑不变

// 原有Nonce Cookie存储逻辑不变
Response.Cookies.Append("Nonce", Convert.ToBase64String(SHA256.HashData(nonce)), new CookieOptions
{
    Path = "/callback",
    HttpOnly = true,
    IsEssential = true,
    Secure = true,
    SameSite = SameSiteMode.Strict,
    Expires = Clock.UtcNow.AddHours(1)
});

// 新增:打包需要跨请求保留的授权上下文,加密后存入Cookie
var flowContext = new Dictionary<string, string>
{
    ["state"] = Convert.ToBase64String(state),
    ["code_verifier"] = Convert.ToBase64String(codeVerifier)
};
var serializedContext = System.Text.Json.JsonSerializer.Serialize(flowContext);
var protectedContext = Protector.Protect(serializedContext);
Response.Cookies.Append("OAuthFlowContext", protectedContext, new CookieOptions
{
    Path = Options.CallbackPath,
    HttpOnly = true,
    IsEssential = true,
    Secure = true,
    SameSite = SameSiteMode.Strict,
    Expires = Clock.UtcNow.AddHours(1)
});

// 原有重定向逻辑不变
Response.Redirect($"{Options.Authority}/authorization?client_id={Options.ClientId}" +
    $"&callback_uri={Request.Scheme}://{Request.Host}{Options.CallbackPath}&scopes={Options.Scopes}" +
    $"&state={Convert.ToBase64String(state)}&nonce={Convert.ToBase64String(nonce)}&code_challenge={Convert.ToBase64String(codeChallenge)}");

2. 在HandleRemoteAuthenticateAsync中取出参数完成校验

回调到达时先解密之前存储的上下文,比对state参数,校验通过后取出code_verifier用于后续换取令牌,校验完成后立即删除临时Cookie防止重放攻击:

protected override async Task<HandleRequestResult> HandleRemoteAuthenticateAsync()
{
    // 从回调查询参数中取返回的state
    if (!Request.Query.TryGetValue("state", out var callbackState))
    {
        return HandleRequestResult.Fail("回调请求缺少state参数");
    }

    // 取之前存储的授权上下文Cookie
    if (!Request.Cookies.TryGetValue("OAuthFlowContext", out var protectedContext))
    {
        return HandleRequestResult.Fail("缺少授权流程上下文Cookie");
    }

    // 解密上下文,解密失败直接返回校验不通过
    string serializedContext;
    try
    {
        serializedContext = Protector.Unprotect(protectedContext);
    }
    catch
    {
        return HandleRequestResult.Fail("授权流程上下文无效");
    }
    var flowContext = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, string>>(serializedContext);
    var originalState = flowContext["state"];
    var codeVerifier = flowContext["code_verifier"];

    // 比对state,不一致说明可能存在CSRF攻击
    if (!string.Equals(originalState, callbackState, StringComparison.Ordinal))
    {
        // 清理无效Cookie
        Response.Cookies.Delete("OAuthFlowContext", new CookieOptions { Path = Options.CallbackPath });
        Response.Cookies.Delete("Nonce", new CookieOptions { Path = "/callback" });
        return HandleRequestResult.Fail("State校验失败,请求可能存在伪造风险");
    }

    // 校验通过后清理临时Cookie,避免重放攻击
    Response.Cookies.Delete("OAuthFlowContext", new CookieOptions { Path = Options.CallbackPath });
    Response.Cookies.Delete("Nonce", new CookieOptions { Path = "/callback" });

    // 后续逻辑:使用回调携带的code、解密得到的codeVerifier调用授权服务器接口换token,构建AuthenticationTicket返回即可
    throw new NotImplementedException();
}

注意事项

  • 禁止明文存储code_verifier这类敏感参数,基类提供的Protector和当前认证方案绑定,加密安全性符合框架标准,直接使用即可。
  • Cookie的Path属性必须严格设置为回调路径,保证只有回调请求会携带这些敏感Cookie,尽可能缩小泄漏范围。
  • 校验流程走完后必须立即删除临时存储的上下文Cookie,避免Cookie被窃取后重放授权请求。
  • 如果不想自己实现Cookie存储逻辑,也可以直接使用基类内置的关联ID(CorrelationId)机制:调用GenerateCorrelationId(properties)方法会自动生成加密state存入Cookie,回调时调用ValidateCorrelationId(properties)即可完成state校验,code_verifier可以存入AuthenticationProperties.Items集合,通过基类的PropertiesDataFormat加密后随state一起传递,和你现有代码逻辑相比改动量稍大,但更符合框架内置的设计规范。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 17:36:28