PAYATHON 2026

Chrome 84 支付跳转后 ASP.NET Web Forms Session 超时

支付阿杰

结论

跨站支付回跳后 Session 丢失,通常是 Chrome 没有发送 Session Cookie,并不一定是服务器端的 Session 真正超时。

支付网关通过跨站 POST 返回网站时,Chrome 84 会按照新版 SameSite 规则处理 Cookie。如果 ASP.NET Web Forms 的 ASP.NET_SessionId 或 Forms Authentication Cookie 没有正确设置为 SameSite=None; Secure,回跳请求可能不会携带这些 Cookie。服务器收到请求后会创建新的 Session,应用就会误判原 Session 已超时,并把用户视为未登录。

建议这样处理:

  1. 将应用升级到至少 .NET Framework 4.7.2。最好使用仍受支持、已安装最新系统更新的 .NET Framework 4.8。
  2. 全站启用 HTTPS。
  3. 将 Session Cookie 和身份验证 Cookie 配置为 SameSite=None; Secure。
  4. 不要只通过 Session 确认支付结果。应使用订单号和支付网关的服务端通知恢复交易上下文。

为什么超过一段时间后才出现

Chrome 会把未声明 SameSite 的 Cookie 默认视为 SameSite=Lax。跨站顶层导航通常可以携带这类 Cookie,但跨站 POST 回跳会受到限制。

部分 Chrome 版本曾为刚创建不久的 Cookie 提供临时兼容机制,允许它在短时间内随跨站 POST 发送。这就可能造成一种现象:用户很快完成支付时一切正常,在支付页面停留较久后,Session 却丢失了。

这个兼容窗口不能作为可靠的业务机制,也不应根据它设置 90 秒或其他超时时间。实际表现还会受到这些因素影响:

  • 支付网关使用 POST 还是 GET 回跳;
  • Session Cookie 的创建时间;
  • Chrome 版本和策略;
  • IIS、负载均衡器或应用程序池的 Session 配置;
  • 支付网关自身的交易超时。

可以在 Chrome 开发者工具的 Network 面板中检查回跳请求。如果请求中没有原来的 ASP.NET_SessionId,基本可以确定问题出在 Cookie 或 SameSite,而不是服务器主动删除了 Session。

推荐配置

以下配置要求运行时已经支持 SameSite。对于 ASP.NET,通常需要使用已安装相应更新的 .NET Framework 4.7.2 或更高版本。

<configuration>
  <system.web>
    <httpCookies
      sameSite="None"
      requireSSL="true" />

    <sessionState
      cookieSameSite="None" />

    <authentication mode="Forms">
      <forms
        loginUrl="~/Login.aspx"
        timeout="30"
        requireSSL="true"
        cookieSameSite="None" />
    </authentication>
  </system.web>
</configuration>

这些配置分别处理不同的 Cookie:

  • sessionState cookieSameSite="None":处理 ASP.NET_SessionId。
  • forms cookieSameSite="None":处理 Forms Authentication Cookie。
  • httpCookies sameSite="None":为应用创建的其他 ASP.NET Cookie 设置默认值。
  • requireSSL="true":为 Cookie 添加 Secure 属性。

现代 Chrome 要求 SameSite=None 必须与 Secure 同时使用,所以站点必须通过 HTTPS 访问。如果仍允许 HTTP 请求,这些请求不会携带 Secure Cookie。

配置完成后,响应中的 Cookie 应类似:

Set-Cookie: ASP.NET_SessionId=...; path=/; secure; HttpOnly; SameSite=None

身份验证 Cookie 也应包含相同的关键属性:

Set-Cookie: .ASPXAUTH=...; path=/; secure; HttpOnly; SameSite=None

.NET Framework 4.5 应如何处理

.NET Framework 4.5 发布时还没有现在的 SameSite 语义,仅在旧版配置中增加一行设置,无法保证它能正确工作。直接在旧运行时中加入:

<httpCookies sameSite="None" />

可能导致配置项无法识别、输出格式错误,或者不同补丁环境下的行为不一致。

更稳妥的做法是升级应用的目标框架,同时确认服务器已安装对应的 .NET Framework 和系统更新。Web Forms 应用从 4.5 升级到 4.7.2 或 4.8 通常不需要重写业务代码,但仍需完整测试以下内容:

  • 登录和退出;
  • Session 创建与续期;
  • 支付网关的成功、失败和取消回跳;
  • Forms Authentication;
  • HTTPS 重定向;
  • 第三方控件及 NuGet 依赖;
  • IIS 应用程序池配置。

如果短期内确实无法升级,可以暂时在响应层手工改写 Set-Cookie,或者通过反向代理添加属性。不过,这类方案比较脆弱,可能重复添加属性、遗漏 Cookie,或者破坏包含特殊字符的 Cookie 头,因此只适合作为临时措施。除非已经验证过所有 Cookie 和客户端范围,否则不要使用简单的字符串拼接统一修改全部响应头。

验证方法

登录后先记录当前 Cookie,再执行一次超过问题时间的支付流程。检查回跳请求的 Request Headers,重点确认是否包含:

Cookie: ASP.NET_SessionId=...

还需要逐项核对:

  1. 回跳前后的 ASP.NET_SessionId 是否相同;
  2. .ASPXAUTH 是否仍被发送;
  3. Cookie 是否显示 SameSite=None;
  4. Cookie 是否显示 Secure;
  5. 回跳地址是否确实使用 HTTPS;
  6. 支付网关是否通过跨站 POST 回跳。

如果原 Session Cookie 已随请求发送,但服务器仍然创建了新 Session,就要继续排查服务端问题,例如:

  • IIS 应用程序池在支付期间发生回收;
  • 使用 InProc Session,而请求被负载均衡到另一台服务器;
  • web.config 或站点文件发生变化,导致应用重启;
  • Session 的 timeout 确实过短;
  • 多节点服务器没有共享 Session State;
  • 机器密钥或 Forms Authentication 配置不一致。

支付流程不要完全依赖 Session

即使 Cookie 配置正确,也不应把 Session 作为支付结果与订单之间的唯一关联方式。用户可能关闭浏览器或禁用 Cookie,支付网关也可能重复发送通知或延迟回调。

发起支付时,应生成不可猜测的交易标识,并将它与订单一起写入数据库:

string transactionId = Guid.NewGuid().ToString("N");

// 保存 transactionId、orderId、userId、金额和初始状态到数据库
// 将 transactionId 作为支付网关允许的商户订单号或 state 参数发送

支付返回后,通过交易标识查询订单,并验证网关签名:

protected void PaymentReturn(object sender, EventArgs e)
{
    string transactionId = Request.Form["transaction_id"];

    if (string.IsNullOrWhiteSpace(transactionId))
    {
        Response.StatusCode = 400;
        return;
    }

    // 1. 根据 transactionId 从数据库加载订单
    // 2. 验证支付网关签名
    // 3. 核对商户号、订单号、金额、币种和支付状态
    // 4. 以幂等方式更新订单
    // 5. Session 仅用于改善用户体验,不作为支付真实性依据
}

字段名称和签名算法必须以具体支付网关的接口文档为准。支付成功状态最好通过服务端通知或主动查询接口确认,浏览器回跳只用于向用户展示结果。

直接的修复方法是升级到受支持的 .NET Framework,并为相关 Cookie 设置 SameSite=None; Secure。从长期来看,支付确认流程还应能够脱离 Session 独立完成。

备注:内容仅供参考。