PAYATHON 2026

HTTP 页面向 HTTPS AJAX POST 时未发送认证 cookie

支付小周

结论

不能通过 Ajax.BeginForm 强制浏览器携带认证 cookie。http:// 页面向 https:// 地址发送 AJAX 请求时,即使主机名和应用相同,也属于跨源请求,因为协议不同,origin 就不同。

最稳妥的做法是让整个页面都使用 HTTPS,并通过相对地址提交表单。如果必须使用跨源 AJAX,就要正确配置 CORS、凭据模式和 cookie 属性。任何一项不符合要求,浏览器都可能不发送 cookie,或者阻止 JavaScript 读取响应。

浏览器会根据以下三项判断请求是否同源:

scheme + host + port

因此,下面两个地址并不同源:

http://same.domain.com
https://same.domain.com

Ajax.BeginForm 只负责生成表单和相应的 AJAX 配置。它无法绕过浏览器的同源策略,也不能读取并手动添加通常带有 HttpOnly 属性的 Forms Authentication cookie。

还要确认示例中的主机名是否真的一致:

same.domain
same.domain.com

如果实际请求使用了不同的主机名,cookie 的 Domain 或 Path 范围也可能阻止浏览器发送它。

推荐方案:整个流程统一使用 HTTPS

将表单所在页面重定向到 HTTPS,并使用相对 URL:

@using (Ajax.BeginForm(
    "Confirm",
    "Payment",
    null,
    new AjaxOptions
    {
        HttpMethod = "POST",
        UpdateTargetId = "myId",
        InsertionMode = InsertionMode.Replace,
        OnBegin = "$('#popupAjaxLoader').show();",
        OnSuccess = "$('#popupAjaxLoader').hide();"
    },
    null))
{
    // fields
}

这样,页面和请求地址的 scheme、host、port 都相同,浏览器就能按照 cookie 自身的规则发送认证信息。

不要在 AjaxOptions.Url 和 HTML 属性中分别设置不同的提交地址。只保留一个明确的 URL 来源,以免最终生成的 action 与 AJAX 实际请求地址不一致。

必须跨源 AJAX 时

如果浏览器和服务器都支持 CORS,可以显式启用凭据请求。下面是 jQuery 示例:

$.ajax({
    url: "https://same.domain.com/payment/confirm",
    type: "POST",
    data: $("#paymentForm").serialize(),
    xhrFields: {
        withCredentials: true
    },
    success: function (result) {
        $("#myId").html(result);
    }
});

服务器也必须返回类似下面的响应头:

Access-Control-Allow-Origin: http://same.domain.com
Access-Control-Allow-Credentials: true

启用凭据后,Access-Control-Allow-Origin 不能设为通配符 *。

认证 cookie 还必须满足当前浏览器的限制。例如,跨站上下文通常需要:

Set-Cookie: AuthCookie=...; Path=/; HttpOnly; Secure; SameSite=None

较旧的 MVC 3、jQuery 或浏览器组合不一定完整支持带凭据的 CORS 流程。Ajax.BeginForm 也没有通用参数能够确保设置 withCredentials,因此可能需要改用自定义 JavaScript 请求。

不需要读取响应时

如果不需要读取响应,可以提交普通的 HTTPS 表单,不必使用 AJAX:

<form method="post" action="https://same.domain.com/payment/confirm">
    <!-- fields -->
    <button type="submit">确认</button>
</form>

普通页面导航不会受到与 AJAX 响应读取相同的限制,但页面会跳转到 HTTPS 地址。

如果业务要求保留当前页面,也可以用 iframe 加载 HTTPS 页面,再在该页面内部提交表单:

<iframe
    src="https://same.domain.com/payment/form"
    name="paymentFrame"
    title="Payment">
</iframe>

父页面仍然无法直接读取 HTTPS iframe 的 DOM 或响应内容。如果两个页面需要通信,双方应明确使用 window.postMessage(),并严格校验 event.origin。

注意事项

不要将认证 cookie、Forms Authentication ticket 或其他长期凭据复制到隐藏字段。用户可以查看和修改隐藏字段,它不能替代安全的身份认证。业务字段还需要经过服务端验证,并配合防 CSRF 机制。

HTTPS 页面在 iframe 中运行时,还可能受到 SameSite、第三方 cookie 限制、X-Frame-Options 或 CSP frame-ancestors 的影响。因此,长期方案仍然应该是全站 HTTPS,而不是让 HTTP 页面跨源调用 HTTPS 接口。

备注:内容仅供参考。