Caloogy 文章
English

同源策略:为什么钱包要单独一个子域名

日期
2026 年 5 月 21 日
系列
Web 安全
阅读
3 分钟
主题
WebSame origin policyXssSubdomain

一句话版本

浏览器里最重要的安全规则是同源策略:协议、域名、端口三者完全相同才算"同一个源"(origin),不同源的页面互相读不到数据。 所以,把最敏感的功能放在一个单独的源上,等于给它建了一道防火墙:同一个网站其他角落出了 XSS 漏洞,也伸不进这个隔离区。这是安全工程里"缩小爆炸半径"的典型做法。

全文分三层:

  1. 通俗理解:同一栋楼和不同的楼
  2. 实际用处:大公司为什么把用户内容放在别的域名上
  3. 原理与工程要点:origin 和 site 的区别、隔离到什么程度才够

第一层:通俗理解

1.1 什么叫"同源"

网址 A 网址 B 同源吗 原因
https://example.com/a https://example.com/b 是 只是路径不同
https://example.com http://example.com 否 协议不同
https://example.com https://app.example.com 否 域名不同
https://example.com https://example.com:8443 否 端口不同

同源的页面就像住在同一个房间:一个页面里的脚本可以随意读写另一个页面的内容、发请求、读 localStorage。不同源的页面则住在不同的房间,默认看不到彼此。

1.2 为什么要搬出去

假设一个网站的登录页、博客、论坛、钱包都放在 example.com 下面,只是路径不同。那么:

把钱包搬到 wallet.example.com,论坛的脚本就碰不到它了。漏洞仍然存在,但伤不到最值钱的东西。


第二层:实际用处

Caloogy Wallet 也采用了这种做法:钱包运行在一个独立的源上。


第三层:原理与工程要点

3.1 同源策略管什么

行为 跨源时
读另一个窗口或 iframe 的 DOM 禁止
读 fetch / XHR 的响应 默认禁止,除非对方用 CORS 明确允许
读 localStorage、IndexedDB 各自独立
发出请求(表单、图片、链接) 允许(这就是 CSRF 存在的原因,见《Cookie安全属性》)
嵌入脚本 <script src> 允许,而且脚本以嵌入者的身份运行(见《CSP与SRI》)

3.2 origin 与 site 的区别

有两处是按 site 而不是按 origin 来算的,因此子域名隔离并不完美:

  1. Cookie:兄弟子站可以设置 Domain=example.com 的 cookie,影响你的子站。对策是用 __Host- 前缀的 cookie。
  2. SameSite:从 blog.example.com 发往 wallet.example.com 的请求被视为"同站",SameSite 挡不住。对策是服务端校验 Origin 头。

极端敏感的应用会干脆用一个完全不同的可注册域名,连 site 都分开。

3.3 隔离不只是浏览器的事

前端搬到单独的源,后端也要相应隔离,否则只是换了个门牌号:

3.4 更强的隔离头

响应头 作用
Cross-Origin-Opener-Policy: same-origin 别的页面用 window.open 打开你后,拿不到你的窗口引用
frame-ancestors 'none'(CSP) 禁止被嵌入 iframe,防点击劫持
Referrer-Policy: no-referrer 跳出时不把当前网址(可能含敏感参数)泄露给对方

总结

层次 要点
直觉 同源 = 同一个房间;敏感功能要单独一个房间
价值 漏洞还在,但爆炸半径变小了
细节 origin ≠ site:cookie 和 SameSite 按 site 算,要额外处理
配套 后端、会话、托管平台也要一起隔离