摘要

  • 2009 年 1 月的 Origin 草案从一条请求说起:浏览器可以告诉服务器是谁触发了请求,服务器据此检查跨站伪造风险。2011 年 12 月的 RFC 6454 把更大的问题写清楚:浏览器到底把哪些页面当成同一个来源。
  • 判定依据是协议、主机和端口,路径不在名单里。同一来源下的用户上传页若作为可运行的 HTML 交给浏览器,就可能取得应用页面的浏览器权限。

门牌分了两层,浏览器只看一层

一家网站把账户页放在 /account,把读者投稿放在 /uploads。后台可能给这两个目录配置不同的访问规则。浏览器却先读地址的前三项:协议、主机、端口。三项相同,便归为同一个来源;斜杠后的字样不会替投稿页划出独立权限区。

2011 年 12 月发布的 RFC 6454记录了这条粗线。它没有声称同源策略在那一年凭空诞生,而是说这套做法已在浏览器中逐步形成,文档为其提供精确定义。若每个 URL 都自成一国,拥有许多页面和脚本的应用要反复申请相互通信。来源三元组让它们能像一个应用那样工作。

代价藏在「一个」里面。同一组地址之下,不只有站长写的页面,还可能有别人递交的内容。RFC 提醒读者留意媒体类型:用户上传资料若被站点当作可执行的 HTML 发布在主来源下,浏览器会让它在那个来源的权限环境里运行。服务器对账户操作仍可另行鉴权;浏览器不会因为路径叫 /uploads 就自动给脚本降级。

端口进了账,路径没进

RFC 的例子很直白。example.com 上的 HTTP 页面,与同一主机上明确写出端口 80 的另一条路径属于同一来源;80 是 HTTP 的默认端口。换一个端口,就换了边界。把 http 改为 https 也一样:同一主机名下,未经加密保护的页面不能直接继承 HTTPS 页面在浏览器里的信任。www 与没有 www 的主机名同样分开。

路径则始终留在圈里。对站长来说,这是一项实际的部署权力:投稿放在哪个主机,走哪个端口,服务器以什么类型把文件交给浏览器,都决定它会站在安全边界的哪侧。目录名写得再像「沙盒」,浏览器也只执行地址和响应共同给出的规则。

2009 年 1 月的 Origin 初稿更关心服务器收到请求时能否知道发起它的安全环境。草案提出 Origin 请求头,指出服务器可以用它防范跨站请求伪造。那份文件是尚未成为标准的 Internet-Draft。RFC 6454 后来把镜头拉远:来源既是请求头可以报告的上下文,也是浏览器在请求发出前就用来归类页面的单位。没有普通三元组的来源在文本中写成 null;RFC 对隐私敏感的请求环境也规定了这个值。

2014 年 W3C 的 CORS 推荐标准使用来源信息判断外站能否读取响应,并明确区分读取响应与批准请求带来的操作。那是后一层许可。更早、更粗的一步,是浏览器先把同一来源里的所有路径放在一间屋子。

RFC 6454 把这间屋子的门框写了下来。端口算数,路径不算。谁被安置在门内,归根结底由网站的发布方式决定。