1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
|
---
title: 浏览器的同源策略
slug: Web/Security/Same-origin_policy
tags:
- AJAX
- CORS
- JavaScript
- Same-origin policy
- Security
- 同源策略
translation_of: Web/Security/Same-origin_policy
---
<p><strong>同源策略</strong>是一个重要的安全策略,它用于限制一个{{Glossary("origin")}}的文档或者它加载的脚本如何能与另一个源的资源进行交互。它能帮助阻隔恶意文档,减少可能被攻击的媒介。</p>
<h2 id="同源的定义"><strong>同源</strong>的<strong>定义</strong></h2>
<p>如果两个 URL 的 {{Glossary("protocol")}}、{{Glossary("port")}} (如果有指定的话)和 {{Glossary("host")}} 都相同的话,则这两个 URL 是<em>同源</em>。这个方案也被称为“协议/主机/端口元组”,或者直接是 “元组”。(“元组” 是指一组项目构成的整体,双重/三重/四重/五重/等的通用形式)。</p>
<p>下表给出了与 URL <code><span class="nowiki">http://store.company.com/dir/page.html</span></code> 的源进行对比的示例:</p>
<table class="standard-table">
<tbody>
<tr>
<th>URL</th>
<th>结果</th>
<th>原因</th>
</tr>
<tr>
<td><code><span class="nowiki">http://store.company.com/dir2/other.html</span></code></td>
<td>同源</td>
<td><font face="consolas, Liberation Mono, courier, monospace">只有路径不同</font></td>
</tr>
<tr>
<td><code><span class="nowiki">http://store.company.com/dir/inner/another.html</span></code></td>
<td>同源</td>
<td><font face="consolas, Liberation Mono, courier, monospace">只有路径不同</font></td>
</tr>
<tr>
<td><code><span class="nowiki">https://store.company.com/secure.html</span></code></td>
<td>失败</td>
<td>协议不同</td>
</tr>
<tr>
<td><code><span class="nowiki">http://store.company.com:81/dir/etc.html</span></code></td>
<td>失败</td>
<td>端口不同 ( <code>http://</code> 默认端口是80)</td>
</tr>
<tr>
<td><code><span class="nowiki">http://news.company.com/dir/other.html</span></code></td>
<td>失败</td>
<td>主机不同</td>
</tr>
</tbody>
</table>
<h3 id="源的继承"><strong>源的</strong><strong>继承</strong></h3>
<p>在页面中通过 <code>about:blank</code> 或 <code>javascript:</code> URL 执行的脚本会继承打开该 URL 的文档的源,因为这些类型的 URLs 没有包含源服务器的相关信息。</p>
<div class="blockIndicator note">
<p>例如,<code>about:blank</code> 通常作为父脚本写入内容的新的空白弹出窗口的 URL(例如,通过 <a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/open">Window.open()</a> )。 如果此弹出窗口也包含 JavaScript,则该脚本将从创建它的脚本那里继承对应的源。</p>
</div>
<div class="note">
<p>注意:在{{Gecko("6.0")}}之前,如果用户在位置栏中输入 <code>data</code> URLs,<code>data</code> URLs 将继承当前浏览器窗口中网页的安全上下文。</p>
</div>
<div class="blockIndicator warning">
<p><code>data</code>:URLs 获得一个新的,空的安全上下文。</p>
</div>
<h3 id="IE_中的特例"><strong>IE 中的特例</strong></h3>
<p>Internet Explorer 的同源策略有两个主要的差异点:</p>
<ul>
<li><strong>授信范围</strong>(Trust Zones):两个相互之间高度互信的域名,如公司域名(corporate domains),则不受同源策略限制。</li>
<li><strong>端口</strong>:IE 未将端口号纳入到同源策略的检查中,因此<span> </span><span><code>https://company.com:81/index.html</code> </span><span>和 </span><span><code>https://company.com/index.html</code> </span>属于同源并且不受任何限制。</li>
</ul>
<p>这些差异点是不规范的,其它浏览器也未做出支持,但会助于开发基于window RT IE的应用程序。</p>
<h2 id="源的更改"><strong>源</strong>的更改</h2>
<p>满足某些限制条件的情况下,页面是可以修改它的源。脚本可以将 {{domxref("document.domain")}} 的值设置为其当前域或其当前域的父域。如果将其设置为其当前域的父域,则这个较短的父域将用于后续源检查。</p>
<p>例如:假设 http://store.company.com/dir/other.html 文档中的一个脚本执行以下语句:</p>
<pre class="eval">document.domain = "company.com";
</pre>
<p>这条语句执行之后,页面将会成功地通过与 <code><span class="nowiki">http://company.com/dir/page.html</span></code> 的同源检测(假设<code>http://company.com/dir/page.html</code> 将其 <code>document.domain</code> 设置为“<code>company.com</code>”,以表明它希望允许这样做 - 更多有关信息,请参阅 {{domxref("document.domain")}} )。然而,<code>company.com</code> 不能设置 <code>document.domain</code> 为 <code>othercompany.com</code>,因为它不是 <code>company.com</code> 的父域。</p>
<p>端口号是由浏览器另行检查的。任何对document.domain的赋值操作,包括 <code>document.domain = document.domain</code> 都会导致端口号被重写为 <code>null</code> 。因此 <code>company.com:8080</code> <strong>不能</strong>仅通过设置 <code>document.domain = "company.com"</code> 来与<code>company.com</code> 通信。必须在他们双方中都进行赋值,以确保端口号都为 <code>null</code> 。</p>
<div class="note">
<p>注意:使用 <code>document.domain</code> 来允许子域安全访问其父域时,您需要在父域和子域中设置 document.domain 为相同的值。这是必要的,即使这样做只是将父域设置回其原始值。不这样做可能会导致权限错误。</p>
</div>
<h2 id="跨源网络访问">跨源网络访问</h2>
<p>同源策略控制不同源之间的交互,例如在使用{{domxref("XMLHttpRequest")}} 或 {{htmlelement("img")}} 标签时则会受到同源策略的约束。这些交互通常分为三类:</p>
<ul>
<li>跨域<em><strong>写操作</strong>(Cross-origin writes)</em>一般是被允许的<em>。</em>例如链接(links),重定向以及表单提交。特定少数的HTTP请求需要添加 <a href="https://developer.mozilla.org/zh-CN/docs/Web/HTTP/CORS#附带身份凭证的请求" title="HTTP/CORS#附带身份凭证的请求">preflight</a>。</li>
<li>跨域<em><strong>资源嵌入</strong>(Cross-origin embedding)</em>一般是被允许(后面会举例说明)。</li>
<li>跨域<em><strong>读操作</strong>(Cross-origin reads)</em>一般是不被允许的<em>,</em>但常可以通过内嵌资源来巧妙的进行读取访问。例如,你可以读取嵌入图片的高度和宽度,调用内嵌脚本的方法,或<a href="https://grepular.com/Abusing_HTTP_Status_Codes_to_Expose_Private_Information">availability of an embedded resource</a>.</li>
</ul>
<p>以下是可能嵌入跨源的资源的一些示例:</p>
<ul>
<li><code><script src="..."></script></code> 标签嵌入跨域脚本。语法错误信息只能被同源脚本中捕捉到。</li>
<li><code><link rel="stylesheet" href="..."></code> 标签嵌入CSS。由于CSS的<a href="http://scarybeastsecurity.blogspot.dk/2009/12/generic-cross-browser-cross-domain.html">松散的语法规则</a>,CSS的跨域需要一个设置正确的 HTTP 头部 <code>Content-Type</code> 。不同浏览器有不同的限制:<span> </span><a href="http://msdn.microsoft.com/zh-CN/library/ie/gg622939%28v=vs.85%29.aspx">IE</a><span>, </span><a href="http://www.mozilla.org/security/announce/2010/mfsa2010-46.html" title="http://www.mozilla.org/security/announce/2010/mfsa2010-46.html">Firefox</a><span>, </span><a href="http://code.google.com/p/chromium/issues/detail?id=9877" title="http://code.google.com/p/chromium/issues/detail?id=9877">Chrome</a><span>, </span><a href="http://support.apple.com/kb/HT4070">Safari</a><span> (跳至CVE-2010-0051)部分 和 </span><a href="http://www.opera.com/support/kb/view/943/">Opera</a>。</li>
<li>通过 {{htmlelement("img")}} 展示的图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,...</li>
<li>通过 {{htmlelement("video")}} 和 {{htmlelement("audio")}} 播放的多媒体资源。</li>
<li>通过 <code><a href="https://developer.mozilla.org/zh-CN/docs/HTML/Element/object" title="HTML/Element/object"><object></a></code>、 <a href="https://developer.mozilla.org/zh-CN/docs/HTML/Element/embed" title="HTML/Element/embed"><code><embed></code></a> 和 <code><a href="https://developer.mozilla.org/zh-CN/docs/HTML/Element/applet" title="HTML/Element/applet"><applet></a></code> 嵌入的插件。</li>
<li>通过 <code><a href="https://developer.mozilla.org/zh-CN/docs/CSS/@font-face" title="CSS/@font-face">@font-face</a></code> 引入的字体。一些浏览器允许跨域字体(cross-origin fonts),一些需要同源字体(same-origin fonts)。</li>
<li><font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">通过</span></font> <code><a href="https://developer.mozilla.org/zh-CN/docs/HTML/Element/iframe" title="HTML/Element/iframe"><iframe></a></code> 载入的任何资源。站点可以使用 <a href="https://developer.mozilla.org/zh-CN/docs/HTTP/X-Frame-Options" title="HTTP/X-Frame-Options">X-Frame-Options</a> 消息头来阻止这种形式的跨域交互。</li>
</ul>
<h3 id="如何允许跨源访问"><strong>如何允许跨源访问</strong></h3>
<p>可以使用 <a href="https://developer.mozilla.org/zh-CN/docs/HTTP/Access_control_CORS">CORS</a> 来允许跨源访问。CORS 是 {{Glossary("HTTP")}} 的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源。</p>
<h3 id="如何阻止跨源访问"><strong>如何阻止跨源访问</strong></h3>
<ul>
<li>阻止跨域写操作,只要检测请求中的一个不可推测的标记(CSRF token)即可,这个标记被称为 <a href="https://www.owasp.org/index.php/Cross-Site_Request_Forgery_%28CSRF%29">Cross-Site Request Forgery (CSRF)</a><span> 标记。你必须使用这个标记来阻止页面的跨站读操作。</span></li>
<li><span>阻止资源的跨站读取,需要保证该资源是不可嵌入的。阻止嵌入行为是必须的,因为嵌入资源通常向其暴露信息。</span></li>
<li><span>阻止跨站嵌入,需要确保你的资源不能通过以上列出的可嵌入资源格式使用。浏览器可能不会遵守 <code>Content-Type</code> 头部定义的类型。例如,如果您在HTML文档中指定 <code><script></code> 标记,则浏览器将尝试将标签内部的 HTML 解析为JavaScript。 当您的资源不是您网站的入口点时,您还可以使用CSRF令牌来防止嵌入。</span></li>
</ul>
<h2 id="跨源脚本API访问">跨源脚本API访问</h2>
<p>JavaScript 的 API 中,如<span> </span><code><a href="https://developer.mozilla.org/zh-CN/docs/DOM/HTMLIFrameElement" title="DOM/HTMLIFrameElement">iframe.contentWindow</a></code>、<span> {{domxref("window.parent")}}、{{domxref("window.open")}} 和 {{domxref("window.opener")}} 允许文档间直接相互引用。当两个文档的源不同时,这些引用方式将对</span><span> </span><a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/browsers.html#security-window">Window</a><span> 和 </span><a href="http://www.whatwg.org/specs/web-apps/current-work/multipage/history.html#security-location">Location</a>对象的访问添加限制,如下两节所述。</p>
<p>为了能让不同源中文档进行交流,可以使用 {{domxref("window.postMessage")}}。</p>
<p>规范: <a href="https://html.spec.whatwg.org/multipage/browsers.html#cross-origin-objects">HTML Living Standard § Cross-origin objects</a> 。</p>
<h3 id="Window">Window</h3>
<p> 允许以下对 <code>Window</code> 属性的跨源访问:</p>
<table class="fullwidth-table standard-table">
<thead>
<tr>
<th scope="col">方法</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{domxref("window.blur")}}</td>
</tr>
<tr>
<td>{{domxref("window.close")}}</td>
</tr>
<tr>
<td>{{domxref("window.focus")}}</td>
</tr>
<tr>
<td>{{domxref("window.postMessage")}}</td>
</tr>
</tbody>
</table>
<table class="fullwidth-table standard-table">
<thead>
<tr>
<th scope="col">属性</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr>
<td>{{domxref("window.closed")}}</td>
<td>只读.</td>
</tr>
<tr>
<td>{{domxref("window.frames")}}</td>
<td>只读.</td>
</tr>
<tr>
<td>{{domxref("window.length")}}</td>
<td>只读.</td>
</tr>
<tr>
<td>{{domxref("window.location")}}</td>
<td>读/写.</td>
</tr>
<tr>
<td>{{domxref("window.opener")}}</td>
<td>只读.</td>
</tr>
<tr>
<td>{{domxref("window.parent")}}</td>
<td>只读.</td>
</tr>
<tr>
<td>{{domxref("window.self")}}</td>
<td>只读.</td>
</tr>
<tr>
<td>{{domxref("window.top")}}</td>
<td>只读.</td>
</tr>
<tr>
<td>{{domxref("window.window")}}</td>
<td>只读.</td>
</tr>
</tbody>
</table>
<p>某些浏览器允许访问除上述外更多的属性。</p>
<h3 id="Location">Location</h3>
<p>允许以下对 <code>Location</code> 属性的跨源访问:</p>
<table class="fullwidth-table standard-table">
<thead>
<tr>
<th scope="col">方法</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{domxref("location.replace")}}</td>
</tr>
</tbody>
</table>
<table class="fullwidth-table standard-table">
<thead>
<tr>
<th scope="col">属性</th>
<th scope="col"></th>
</tr>
</thead>
<tbody>
<tr>
<td>{{domxref("URLUtils.href")}}</td>
<td>只写.</td>
</tr>
</tbody>
</table>
<p>某些浏览器允许访问除上述外更多的属性。</p>
<h2 id="跨源数据存储访问">跨源数据存储访问</h2>
<p>访问存储在浏览器中的数据,如 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/Window/localStorage">localStorage</a> 和 <a href="https://developer.mozilla.org/zh-CN/docs/IndexedDB">IndexedDB</a>,是以源进行分割。每个源都拥有自己单独的存储空间,一个源中的 JavaScript 脚本不能对属于其它源的数据进行读写操作。</p>
<p>{{glossary("Cookie", "Cookies")}} 使用不同的源定义方式。一个页面可以为本域和其父域设置 cookie,只要是父域不是公共后缀(public suffix)即可。Firefox 和 Chrome 使用 <a href="http://publicsuffix.org/">Public Suffix List</a> 检测一个域是否是公共后缀(public suffix)。Internet Explorer 使用其内部的方法来检测域是否是公共后缀。不管使用哪个协议(HTTP/HTTPS)或端口号,浏览器都允许给定的域以及其任何子域名(sub-domains) 访问 cookie。当你设置 cookie 时,你可以使用 <code>Domain</code>、<code>Path</code>、<code>Secure</code>、和 <code>HttpOnly</code> 标记来限定其可访问性。当你读取 cookie 时,你无法知道它是在哪里被设置的。 即使您只使用安全的 https 连接,您看到的任何 cookie 都有可能是使用不安全的连接进行设置的。</p>
<h2 id="参见">参见</h2>
<ul>
<li><a href="http://www.w3.org/Security/wiki/Same_Origin_Policy">Same-Origin Policy at W3C</a></li>
<li><a href="http://web.dev/secure/same-origin-policy">http://web.dev/secure/same-origin-policy</a></li>
</ul>
<div class="originaldocinfo">
<h2 id="Original_Document_Information" name="Original_Document_Information">原始文件资料</h2>
<ul>
<li>Author(s): Jesse Ruderman</li>
</ul>
</div>
<div class="noinclude"></div>
|