为什么表单提交不会跨域
同源策略与跨域概述
在Web安全领域,同源策略(Same-Origin Policy)是一种关键的安全机制,它限制了一个源(协议+域名+端口)的文档或脚本如何与另一个源的资源进行交互。这个策略的目的是为了防止恶意网站读取另一个网站的敏感数据,从而保护用户信息安全。
同源策略主要表现在三个层面:
- DOM层面:限制了不同源的JavaScript脚本对当前DOM对象的读写操作
- 数据层面:限制了不同源的站点读取当前站点的Cookie、IndexDB、LocalStorage等数据
- 网络层面:限制了通过XMLHttpRequest或Fetch等API将站点的数据发送给不同源的站点
表单提交与同源策略的关系
虽然同源策略限制了JavaScript通过XMLHttpRequest或Fetch API进行跨域请求,但有趣的是,HTML表单(form)提交到不同源的服务器却不受此限制。这看似是同源策略的一个”漏洞”,但实际上是Web平台设计的一部分。
为什么表单可以跨域提交?
-
历史原因:表单提交是早期Web的基础功能,在同源策略制定前就已存在,为了保持向后兼容性而被保留
-
安全模型差异:同源策略主要关注的是读取操作而非写入操作
- 表单提交是一种”写入”操作,它向服务器发送数据,但不能读取响应内容
- 当表单提交到跨域服务器时,浏览器会导航到新页面或刷新当前页面,原页面的JavaScript无法读取响应内容
-
用户交互:传统上,表单提交通常需要用户主动触发(如点击提交按钮),这被认为是一种明确的用户意图表达
表单提交与XMLHttpRequest/Fetch的区别
特性 | 表单提交 | XMLHttpRequest/Fetch |
---|---|---|
跨域能力 | 可以直接跨域提交 | 默认受同源策略限制,需要CORS支持 |
响应处理 | 无法直接获取响应内容 | 可以读取响应内容并进行处理 |
页面影响 | 默认会导致页面刷新或跳转 | 不会导致页面刷新(异步) |
请求控制 | 有限的请求方法和头部控制 | 可以精细控制请求方法、头部等 |
数据格式 | 主要支持表单编码数据 | 支持多种数据格式(JSON、XML等) |
表单跨域提交的安全风险
虽然表单可以跨域提交,但这也带来了安全风险,最典型的就是CSRF(跨站请求伪造)攻击:
CSRF攻击原理
- 用户登录了目标网站A并获得了认证Cookie
- 用户在不登出A的情况下访问了恶意网站B
- 恶意网站B包含一个自动提交到网站A的表单
- 当表单提交时,浏览器会自动携带网站A的Cookie
- 网站A的服务器收到请求后,验证Cookie有效,执行了操作
<!-- 恶意网站上的隐藏表单示例 -->
<form id="evil-form" action="https://bank.com/transfer" method="POST" style="display:none">
<input type="hidden" name="to" value="hacker-account" />
<input type="hidden" name="amount" value="10000" />
</form>
<script>
document.getElementById("evil-form").submit(); // 自动提交表单
</script>
防护措施
为了防止表单跨域提交带来的安全风险,可以采取以下措施:
1. CSRF Token
最常用的防护措施是使用CSRF Token:
<form action="/transfer" method="post">
<input type="hidden" name="csrf_token" value="随机生成的令牌" />
<!-- 其他表单字段 -->
</form>
服务器验证这个令牌是否有效,由于同源策略限制,恶意网站无法读取到这个令牌,因此无法构造有效的请求。
2. SameSite Cookie属性
现代浏览器支持为Cookie设置SameSite属性,限制Cookie在跨站请求中的发送:
Set-Cookie: session=abc123; SameSite=Strict
Strict
:完全禁止第三方网站发送该CookieLax
:允许导航到目标网站的GET请求发送Cookie,但禁止其他跨站请求发送None
:允许跨站请求发送Cookie(需要同时设置Secure属性)
3. 验证请求来源
服务器可以检查HTTP请求头中的Origin
或Referer
字段,验证请求是否来自可信来源:
// 服务器端验证示例(伪代码)
const origin = request.headers.origin;
const allowedOrigins = ['https://example.com', 'https://sub.example.com'];
if (!allowedOrigins.includes(origin)) {
return response.status(403).send('Forbidden');
}
4. 要求用户再次验证敏感操作
对于敏感操作(如转账、修改密码等),可以要求用户再次输入密码或进行其他形式的身份验证。
总结
表单提交不受跨域限制主要是因为:
- 历史兼容性考虑
- 同源策略主要限制的是”读取”而非”写入”操作
- 表单提交后无法直接获取响应内容
虽然这种设计为Web应用提供了便利,但也带来了安全风险。开发者需要采取适当的防护措施,如CSRF Token、SameSite Cookie等,来保护Web应用免受跨站请求伪造等攻击。
理解表单提交与同源策略的关系,对于前端开发者来说是非常重要的,它有助于我们构建更安全的Web应用。