跳到主要内容

3. 同源策略原则 (Principles of the Same-Origin Policy)

3. 同源策略原则 (Principles of the Same-Origin Policy)​

许多用户代理会代表远程一方执行动作. 例如, HTTP 用户代理会遵循重定向, 而重定向是远程服务器给出的指令; HTML 用户代理则会把丰富的 Document Object Model (DOM) 接口暴露给从远程服务器取得的脚本.

如果没有任何安全模型, 用户代理可能会执行对用户或其他一方有害的动作. 随着时间推移, 许多 Web 相关技术逐渐收敛到一个通用安全模型, 俗称 "same-origin policy". 虽然这个安全模型很大程度上是自然演进而来, 但同源策略可以用少数几个关键概念来理解. 本节介绍这些概念, 并给出如何安全使用这些概念的建议.

3.1 信任 (Trust)​

同源策略通过 URI 指定信任. 例如, HTML 文档使用 URI 指定要运行的脚本:

<script src="https://example.com/library.js"></script>

当用户代理处理此元素时, 用户代理会获取指定 URI 处的脚本, 并以该文档的权限执行该脚本. 通过这种方式, 文档把它拥有的全部权限授予 URI 所指定的资源. 本质上, 文档声明它信任从该 URI 取得的信息的完整性.

除了从 URI 导入库之外, 用户代理还会把信息发送给 URI 指定的远程一方. 例如, 考虑以下 HTML form 元素:

<form method="POST" action="https://example.com/login">
... <input type="password"> ...
</form>

当用户输入密码并提交表单时, 用户代理会把密码发送到 URI 指定的网络端点. 通过这种方式, 文档把它的秘密数据导出到该 URI, 本质上声明它信任发送到该 URI 的信息的机密性.

3.1.1 陷阱 (Pitfalls)​

设计使用同源策略的新协议时, 应确保重要的信任差异在 URI 中可见. 例如, 如果受 Transport Layer Security (TLS) 保护的资源和未受 TLS 保护的资源都使用 "http" URI scheme (如 [RFC2817] 中那样), 文档就无法指定它只希望通过 TLS 取得脚本. 通过使用 "https" URI scheme, 文档能够表明它希望与受主动网络攻击者防护的资源交互.

3.2 源 (Origin)​

原则上, 用户代理可以把每个 URI 都视为独立保护域, 并要求从一个 URI 取得的内容与另一个 URI 交互时必须获得明确同意. 不幸的是, 这种设计对开发者很笨重, 因为 Web 应用通常由多个协同工作的资源组成.

取而代之, 用户代理把 URI 分组成称为 "origin" 的保护域. 粗略地说, 如果两个 URI 具有相同的 scheme, host 和 port, 它们就属于同一个源 (即表示同一个主体). 完整细节见第 4 节.

问: 为什么不只使用 host?

答: 在源元组中包含 scheme 对安全至关重要. 如果用户代理不包含 scheme, http://example.com 与 https://example.com 之间就不会有隔离, 因为二者具有相同 host. 但是, 如果没有这种隔离, 主动网络攻击者就可以篡改从 http://example.com 取得的内容, 并让这些内容指示用户代理破坏从 https://example.com 取得内容的机密性和完整性, 从而绕过 TLS [RFC5246] 提供的保护.

问: 为什么使用完全限定主机名, 而不是只使用 "top-level" 域?

答: 虽然 DNS 具有分层委派, 但主机名之间的信任关系会随部署而变化. 例如, 在许多教育机构中, 学生可以在 https://example.edu/~student/ 托管内容, 但这并不意味着由学生编写的文档应当与托管在 https://grades.example.edu/ 的成绩管理 Web 应用属于同一个源 (即处于同一个保护域).

example.edu 部署说明, 按源对资源分组并不总是与每种部署场景完全一致. 在该部署中, 每个学生的网站都处于同一个源, 这可能并不理想. 从某种意义上说, 源的粒度是安全模型演进方式留下的历史产物.

3.2.1 示例 (Examples)​

以下所有资源具有相同源:

http://example.com/
http://example.com:80/
http://example.com/path/file

这些 URI 都具有相同的 scheme, host 和 port 组件.

以下每个资源与其他资源的源都不同:

http://example.com/
http://example.com:8080/
http://www.example.com/
https://example.com:80/
https://example.com/
http://example.org/
http://ietf.org/

在每种情况下, scheme, host 和 port 组件中至少有一个不同于列表中的其他资源.

3.3 权限 (Authority)​

虽然用户代理把 URI 分组成源, 但一个源中的每个资源并不都具有相同权限 (这里的 "authority" 是安全意义上的权限, 而不是 [RFC3986] 意义上的 authority). 例如, 图像是被动内容, 因而不携带权限, 这意味着图像不能访问其源可用的对象和资源. 相比之下, HTML 文档携带其源的全部权限, 文档内的脚本 (或导入到文档中的脚本) 可以访问其源中的每个资源.

用户代理通过检查资源的媒体类型来决定授予资源多少权限. 例如, 媒体类型为 image/png 的资源被视为图像, 媒体类型为 text/html 的资源被视为 HTML 文档.

托管不可信内容 (例如用户生成内容) 时, Web 应用可以通过限制其媒体类型来限制该内容的权限. 例如, 将用户生成内容作为 image/png 提供, 比作为 text/html 提供风险更低. 当然, 许多 Web 应用会在其 HTML 文档中包含不可信内容. 如果处理不谨慎, 这些应用就有把其源权限泄漏给不可信内容的风险, 这种漏洞通常称为跨站脚本.

3.3.1 陷阱 (Pitfalls)​

设计 Web 平台的新组成部分时, 应小心不要无视媒体类型而向资源授予权限. 许多 Web 应用会用受限媒体类型提供不可信内容. 如果新的 Web 平台特性向这些内容授予权限, 就有可能给现有应用引入漏洞. 因此, 更适合向已经拥有该源全部权限的媒体类型授予权限, 或者向专门为携带新权限而设计的新媒体类型授予权限.

为了保持与提供错误媒体类型的服务器兼容, 一些用户代理会使用 "content sniffing", 并把内容当作不同于服务器所提供媒体类型的媒体类型来处理. 如果处理不谨慎, 内容嗅探可能导致安全漏洞, 因为用户代理可能会把低权限媒体类型 (例如图像) 授予高权限媒体类型 (例如 HTML 文档) 的特权 [SNIFF].

3.4 策略 (Policy)​

一般而言, 用户代理会隔离不同源, 并允许源之间进行受控通信. 用户代理如何提供隔离和通信的细节取决于多个因素.

3.4.1 对象访问 (Object Access)​

用户代理暴露的大多数对象 (也称为应用程序编程接口或 API) 只对同源可用. 具体而言, 从一个 URI 取得的内容可以访问与另一个 URI 取得内容相关联的对象, 当且仅当这两个 URI 属于同一个源, 例如具有相同 scheme, host 和 port.

这条通用规则存在一些例外. 例如, HTML 的 Location 接口的某些部分可跨源使用 (例如, 用于允许导航其他浏览上下文). 又如, HTML 的 postMessage 接口明确跨源可见, 以便支持跨源通信. 向外部源暴露对象是危险的, 只能非常谨慎地进行, 因为这样做会把这些对象暴露给潜在攻击者.

3.4.2 网络访问 (Network Access)​

对网络资源的访问取决于这些资源是否与试图访问它们的内容属于同一个源.

一般来说, 禁止读取来自另一个源的信息. 但是, 一个源可以使用从其他源取得的某些类型资源. 例如, 一个源可以执行来自任意源的脚本, 渲染图像并应用样式表. 同样, 一个源也可以显示来自另一个源的内容, 例如 HTML frame 中的 HTML 文档. 网络资源也可以选择允许其他源读取其信息, 例如使用 Cross-Origin Resource Sharing [CORS]. 在这些情况下, 访问通常按每个源授予.

允许向另一个源发送信息. 但是, 通过网络以任意格式发送信息是危险的. 因此, 用户代理会限制文档只能使用特定协议发送信息, 例如不带自定义头部字段的 HTTP 请求. 扩展允许的协议集合, 例如增加对 WebSockets 的支持, 必须谨慎进行, 以避免引入漏洞 [RFC6455].

3.4.3 陷阱 (Pitfalls)​

每当用户代理允许一个源与另一个源的资源交互时, 都会引入安全问题. 例如, 显示来自另一个源的图像会泄漏其高度和宽度. 类似地, 向另一个源发送网络请求的能力会导致跨站请求伪造漏洞 [CSRF]. 但是, 用户代理实现者通常会在这些风险与允许跨源交互的收益之间进行权衡. 例如, 如果 HTML 用户代理阻止跨源网络请求, 就会阻止用户跟随超链接, 而这是 Web 的核心功能.

向 Web 平台添加新功能时, 可能会诱使人们把某项特权授予一个资源, 但不给同一源中的另一个资源授予该特权. 然而, 以这种方式保留特权是无效的, 因为没有该特权的资源通常仍然可以获得该特权, 因为用户代理不会隔离同一个源内的资源. 因此, 特权应当针对整个源授予或保留 (而不是在同一个源内的单个资源之间作区分) [BOFGO].

3.5 结论 (Conclusion)​

同源策略使用 URI 指定信任关系. URI 被分组成源, 源表示保护域. 源中的某些资源 (例如主动内容) 被授予该源的全部权限, 而源中的其他资源 (例如被动内容) 不会被授予该源的权限. 携带其源权限的内容被授予对其自身源内对象和网络资源的访问权. 该内容也被授予对其他源对象和网络资源的有限访问权, 但这些跨源特权必须谨慎设计, 以避免安全漏洞.