K 的一隅

浏览器 浏览器核心机制

同一个网页,为什么不能随便读取另一个网页:浏览器安全边界

从办公楼门禁和跨公司传话说起,理解同源策略、CORS、postMessage、Cookie 安全、XSS 与 CSRF。

4 分钟阅读 更新于 2026-07-24
本文目录

办公楼的门禁允许你把文件递给前台,却不允许你直接打开隔壁公司的抽屉。网页也需要类似边界:一个来源可以向另一个来源发起某些请求,但不能随便读取对方的 DOM、存储和响应内容。

同源到底比较什么

同源是脚本权限的边界,不是“两个页面能不能互相发网络请求”的简单开关。

same-origin policy(同源策略)通常比较协议、主机和端口:

text
https://app.example.com:443/page
协议 https
主机 app.example.com
端口 443

只要这三项有一项不同,浏览器就可能把两个页面视为不同来源。https://example.comhttp://example.com 不同,app.example.comapi.example.com 不同,8080 与默认端口也不同。

同源策略不是“完全不能联系”。页面可以加载图片、脚本或发起网络请求,只是读取能力和执行权限受不同规则约束。把“能发请求”与“能读响应”混为一谈,是跨域排查最常见的第一步误判。

CORS 是谁在授权

CORS(Cross-Origin Resource Sharing,跨源资源共享)主要是服务器通过响应头告诉浏览器:某个跨源页面可以读取这个响应:

http
Access-Control-Allow-Origin: https://app.example.com

前端代码不能靠加一个请求头就“打开跨域”。浏览器发送简单请求后,会检查服务器返回的允许来源;复杂请求可能先发 OPTIONS 预检,询问方法、请求头和凭证是否被允许。

js
const response = await fetch('https://api.example.com/data', {
  headers: { 'X-Client': 'web' },
})

如果服务器没有正确回应 CORS,浏览器会阻止脚本读取响应。服务器可能实际上已经处理了请求,但页面只能看到一个跨源读取错误。这就是“服务器收到”和“浏览器把内容交给脚本”是两层问题。

带 Cookie 的跨源请求还需要明确 credentials 和服务器的允许凭证响应头;不能把 Access-Control-Allow-Origin: * 与凭证随意组合。CORS 解决读取授权,不是身份认证,也不是 CSRF 防护的全部。

iframe 和 postMessage

页面可以把另一个来源放进 iframe,但跨源父页面不能直接访问 iframe 的 DOM。双方若确实需要通信,应使用 postMessage

js
iframe.contentWindow.postMessage(
  { type: 'resize', height: 480 },
  'https://widget.example.com',
)

window.addEventListener('message', (event) => {
  if (event.origin !== 'https://widget.example.com') return
  if (event.data?.type === 'resize') resizeFrame(event.data.height)
})

targetOrigin 不是装饰,它限制消息应该送给谁;接收方必须检查 event.origin,必要时检查 event.source、消息类型和数据结构。只判断“收到了一条 message”就执行操作,相当于门卫只看见有人递来信封,却不看公司和内容。

不要把 '*' 当成跨域调试的永久值。发送敏感数据时要写明确来源;接收后还要做业务校验,因为合法来源的页面也可能被攻击或被错误配置。

Cookie 可能由浏览器在请求时自动携带,所以它和 localStorage token 的威胁形状不同。SameSite=LaxStrictNone 会影响跨站请求携带;Secure 通常与 SameSite=None 一起要求 HTTPS。

CSRF(Cross-Site Request Forgery,跨站请求伪造)利用的正是“浏览器自动带上身份凭证”这一点:攻击页面不一定能读取响应,却可能诱导浏览器发出有副作用的请求。防护通常需要 SameSite、CSRF token、检查来源或改变请求设计。CORS 失败并不等于 CSRF 不存在,因为请求可能已到达服务器。

XSS 是另一种问题

XSS(Cross-Site Scripting,跨站脚本)是攻击者让不应执行的脚本进入当前来源。它可能直接读取页面可见数据、localStorage,或以当前用户权限调用接口。简单地说:CORS 在处理不同来源之间的读取边界,XSS 在处理当前来源里哪些内容能变成代码。

js
// 不安全:把用户输入当 HTML 插入
container.innerHTML = userInput

// 更安全的默认选择:当作纯文本
container.textContent = userInput

真实项目还要配合输出编码、严格的模板默认转义、Content Security Policy 和服务器端校验。不要把“只要不是跨域就安全”当成结论;同源脚本一旦被注入,门禁边界就等于被内部人员绕开。

sandbox iframe 的一层保险

html
<iframe
  src="https://example.com/widget"
  sandbox="allow-scripts"
  referrerpolicy="no-referrer"
></iframe>

sandbox 可以限制 iframe 的脚本、表单、弹窗、同源身份等能力。具体允许项要按功能最小化;把所有 allow-* 都打开,保护效果就会下降。嵌入第三方内容时,要同时考虑消息白名单、资源加载策略和数据能否被它看到。

容易踩的坑

mode: 'no-cors' 拿不到可读正文,通常只得到 opaque response;CORS 授权主要由服务器响应头表达,不是前端一厢情愿能“打开”。同源策略也挡不住所有跨站请求——CSRF 正是利用“请求可能发出、响应不可读”。跨源 iframe 之间更不能互相乱摸 DOM,需要经过校验的 postMessage