OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/child/site_isolation_policy.h" | 5 #include "content/child/site_isolation_policy.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
9 #include "base/lazy_instance.h" | 9 #include "base/lazy_instance.h" |
10 #include "base/logging.h" | 10 #include "base/logging.h" |
11 #include "base/metrics/histogram.h" | 11 #include "base/metrics/histogram.h" |
12 #include "base/strings/string_util.h" | 12 #include "base/strings/string_util.h" |
13 #include "content/public/common/content_switches.h" | 13 #include "content/public/common/content_switches.h" |
14 #include "content/public/common/resource_response_info.h" | 14 #include "content/public/common/resource_response_info.h" |
15 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" | 15 #include "net/base/registry_controlled_domains/registry_controlled_domain.h" |
16 #include "net/http/http_response_headers.h" | 16 #include "net/http/http_response_headers.h" |
17 | 17 |
18 using base::StringPiece; | 18 using base::StringPiece; |
19 | 19 |
20 namespace content { | 20 namespace content { |
21 | 21 |
22 namespace { | 22 namespace { |
23 | 23 |
24 // The cross-site document blocking/UMA data collection is deactivated by | 24 // The gathering of UMA stats for site isolation is deactivated by default, and |
25 // default, and only activated in renderer processes. | 25 // only activated in renderer processes. |
26 static bool g_policy_enabled = false; | 26 static bool g_stats_gathering_enabled = false; |
27 | 27 |
28 // MIME types | 28 // MIME types |
29 const char kTextHtml[] = "text/html"; | 29 const char kTextHtml[] = "text/html"; |
30 const char kTextXml[] = "text/xml"; | 30 const char kTextXml[] = "text/xml"; |
31 const char xAppRssXml[] = "application/rss+xml"; | 31 const char xAppRssXml[] = "application/rss+xml"; |
32 const char kAppXml[] = "application/xml"; | 32 const char kAppXml[] = "application/xml"; |
33 const char kAppJson[] = "application/json"; | 33 const char kAppJson[] = "application/json"; |
34 const char kTextJson[] = "text/json"; | 34 const char kTextJson[] = "text/json"; |
35 const char kTextXjson[] = "text/x-json"; | 35 const char kTextXjson[] = "text/x-json"; |
36 const char kTextPlain[] = "text/plain"; | 36 const char kTextPlain[] = "text/plain"; |
(...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
124 IncrementHistogramCount(bucket_prefix + ".NotBlocked"); | 124 IncrementHistogramCount(bucket_prefix + ".NotBlocked"); |
125 if (sniffed_as_js) | 125 if (sniffed_as_js) |
126 IncrementHistogramCount(bucket_prefix + ".NotBlocked.MaybeJS"); | 126 IncrementHistogramCount(bucket_prefix + ".NotBlocked.MaybeJS"); |
127 } | 127 } |
128 | 128 |
129 } // namespace | 129 } // namespace |
130 | 130 |
131 SiteIsolationResponseMetaData::SiteIsolationResponseMetaData() { | 131 SiteIsolationResponseMetaData::SiteIsolationResponseMetaData() { |
132 } | 132 } |
133 | 133 |
134 void SiteIsolationPolicy::SetPolicyEnabled(bool enabled) { | 134 void SiteIsolationStatsGatherer::SetEnabled(bool enabled) { |
135 g_policy_enabled = enabled; | 135 g_stats_gathering_enabled = enabled; |
136 } | 136 } |
137 | 137 |
138 linked_ptr<SiteIsolationResponseMetaData> | 138 linked_ptr<SiteIsolationResponseMetaData> |
139 SiteIsolationPolicy::OnReceivedResponse(const GURL& frame_origin, | 139 SiteIsolationStatsGatherer::OnReceivedResponse( |
140 const GURL& response_url, | 140 const GURL& frame_origin, |
141 ResourceType resource_type, | 141 const GURL& response_url, |
142 int origin_pid, | 142 ResourceType resource_type, |
143 const ResourceResponseInfo& info) { | 143 int origin_pid, |
144 if (!g_policy_enabled) | 144 const ResourceResponseInfo& info) { |
145 if (!g_stats_gathering_enabled) | |
145 return linked_ptr<SiteIsolationResponseMetaData>(); | 146 return linked_ptr<SiteIsolationResponseMetaData>(); |
146 | 147 |
147 // if |origin_pid| is non-zero, it means that this response is for a plugin | 148 // if |origin_pid| is non-zero, it means that this response is for a plugin |
148 // spawned from this renderer process. We exclude responses for plugins for | 149 // spawned from this renderer process. We exclude responses for plugins for |
149 // now, but eventually, we're going to make plugin processes directly talk to | 150 // now, but eventually, we're going to make plugin processes directly talk to |
150 // the browser process so that we don't apply cross-site document blocking to | 151 // the browser process so that we don't apply cross-site document blocking to |
151 // them. | 152 // them. |
152 if (origin_pid) | 153 if (origin_pid) |
153 return linked_ptr<SiteIsolationResponseMetaData>(); | 154 return linked_ptr<SiteIsolationResponseMetaData>(); |
154 | 155 |
155 UMA_HISTOGRAM_COUNTS("SiteIsolation.AllResponses", 1); | 156 UMA_HISTOGRAM_COUNTS("SiteIsolation.AllResponses", 1); |
156 | 157 |
157 // See if this is for navigation. If it is, don't block it, under the | 158 // See if this is for navigation. If it is, don't block it, under the |
158 // assumption that we will put it in an appropriate process. | 159 // assumption that we will put it in an appropriate process. |
159 if (IsResourceTypeFrame(resource_type)) | 160 if (IsResourceTypeFrame(resource_type)) |
160 return linked_ptr<SiteIsolationResponseMetaData>(); | 161 return linked_ptr<SiteIsolationResponseMetaData>(); |
161 | 162 |
162 if (!IsBlockableScheme(response_url)) | 163 if (!CrossSiteDocumentClassifier::IsBlockableScheme(response_url)) |
163 return linked_ptr<SiteIsolationResponseMetaData>(); | 164 return linked_ptr<SiteIsolationResponseMetaData>(); |
164 | 165 |
165 if (IsSameSite(frame_origin, response_url)) | 166 if (CrossSiteDocumentClassifier::IsSameSite(frame_origin, response_url)) |
166 return linked_ptr<SiteIsolationResponseMetaData>(); | 167 return linked_ptr<SiteIsolationResponseMetaData>(); |
167 | 168 |
168 SiteIsolationResponseMetaData::CanonicalMimeType canonical_mime_type = | 169 SiteIsolationResponseMetaData::CanonicalMimeType canonical_mime_type = |
169 GetCanonicalMimeType(info.mime_type); | 170 CrossSiteDocumentClassifier::GetCanonicalMimeType(info.mime_type); |
170 | 171 |
171 if (canonical_mime_type == SiteIsolationResponseMetaData::Others) | 172 if (canonical_mime_type == SiteIsolationResponseMetaData::Others) |
172 return linked_ptr<SiteIsolationResponseMetaData>(); | 173 return linked_ptr<SiteIsolationResponseMetaData>(); |
173 | 174 |
174 // Every CORS request should have the Access-Control-Allow-Origin header even | 175 // Every CORS request should have the Access-Control-Allow-Origin header even |
175 // if it is preceded by a pre-flight request. Therefore, if this is a CORS | 176 // if it is preceded by a pre-flight request. Therefore, if this is a CORS |
176 // request, it has this header. response.httpHeaderField() internally uses | 177 // request, it has this header. response.httpHeaderField() internally uses |
177 // case-insensitive matching for the header name. | 178 // case-insensitive matching for the header name. |
178 std::string access_control_origin; | 179 std::string access_control_origin; |
179 | 180 |
180 // We can use a case-insensitive header name for EnumerateHeader(). | 181 // We can use a case-insensitive header name for EnumerateHeader(). |
181 info.headers->EnumerateHeader(NULL, "access-control-allow-origin", | 182 info.headers->EnumerateHeader(NULL, "access-control-allow-origin", |
182 &access_control_origin); | 183 &access_control_origin); |
183 if (IsValidCorsHeaderSet(frame_origin, response_url, access_control_origin)) | 184 if (CrossSiteDocumentClassifier::IsValidCorsHeaderSet( |
185 frame_origin, response_url, access_control_origin)) | |
184 return linked_ptr<SiteIsolationResponseMetaData>(); | 186 return linked_ptr<SiteIsolationResponseMetaData>(); |
185 | 187 |
186 // Real XSD data collection starts from here. | 188 // Real XSD data collection starts from here. |
187 std::string no_sniff; | 189 std::string no_sniff; |
188 info.headers->EnumerateHeader(NULL, "x-content-type-options", &no_sniff); | 190 info.headers->EnumerateHeader(NULL, "x-content-type-options", &no_sniff); |
189 | 191 |
190 linked_ptr<SiteIsolationResponseMetaData> resp_data( | 192 linked_ptr<SiteIsolationResponseMetaData> resp_data( |
191 new SiteIsolationResponseMetaData); | 193 new SiteIsolationResponseMetaData); |
192 resp_data->frame_origin = frame_origin.spec(); | 194 resp_data->frame_origin = frame_origin.spec(); |
193 resp_data->response_url = response_url; | 195 resp_data->response_url = response_url; |
194 resp_data->resource_type = resource_type; | 196 resp_data->resource_type = resource_type; |
195 resp_data->canonical_mime_type = canonical_mime_type; | 197 resp_data->canonical_mime_type = canonical_mime_type; |
196 resp_data->http_status_code = info.headers->response_code(); | 198 resp_data->http_status_code = info.headers->response_code(); |
197 resp_data->no_sniff = LowerCaseEqualsASCII(no_sniff, "nosniff"); | 199 resp_data->no_sniff = LowerCaseEqualsASCII(no_sniff, "nosniff"); |
198 | 200 |
199 return resp_data; | 201 return resp_data; |
200 } | 202 } |
201 | 203 |
202 bool SiteIsolationPolicy::OnReceivedFirstChunk( | 204 bool SiteIsolationStatsGatherer::OnReceivedFirstChunk( |
203 const linked_ptr<SiteIsolationResponseMetaData>& resp_data, | 205 const linked_ptr<SiteIsolationResponseMetaData>& resp_data, |
204 const char* raw_data, | 206 const char* raw_data, |
205 int raw_length) { | 207 int raw_length) { |
206 if (!g_policy_enabled) | 208 if (!g_stats_gathering_enabled) |
207 return false; | 209 return false; |
208 | 210 |
209 DCHECK(resp_data.get()); | 211 DCHECK(resp_data.get()); |
210 | 212 |
211 StringPiece data(raw_data, raw_length); | 213 StringPiece data(raw_data, raw_length); |
212 | 214 |
213 // Record the length of the first received chunk of data to see if it's enough | 215 // Record the length of the first received chunk of data to see if it's enough |
214 // for sniffing. | 216 // for sniffing. |
215 UMA_HISTOGRAM_COUNTS("SiteIsolation.XSD.DataLength", raw_length); | 217 UMA_HISTOGRAM_COUNTS("SiteIsolation.XSD.DataLength", raw_length); |
216 | 218 |
217 // Record the number of cross-site document responses with a specific mime | 219 // Record the number of cross-site document responses with a specific mime |
218 // type (text/html, text/xml, etc). | 220 // type (text/html, text/xml, etc). |
219 UMA_HISTOGRAM_ENUMERATION( | 221 UMA_HISTOGRAM_ENUMERATION( |
220 "SiteIsolation.XSD.MimeType", resp_data->canonical_mime_type, | 222 "SiteIsolation.XSD.MimeType", resp_data->canonical_mime_type, |
221 SiteIsolationResponseMetaData::MaxCanonicalMimeType); | 223 SiteIsolationResponseMetaData::MaxCanonicalMimeType); |
222 | 224 |
223 // Store the result of cross-site document blocking analysis. | 225 // Store the result of cross-site document blocking analysis. |
224 bool would_block = false; | 226 bool would_block = false; |
225 bool sniffed_as_js = SniffForJS(data); | 227 bool sniffed_as_js = SniffForJS(data); |
226 | 228 |
227 // Record the number of responses whose content is sniffed for what its mime | 229 // Record the number of responses whose content is sniffed for what its mime |
228 // type claims it to be. For example, we apply a HTML sniffer for a document | 230 // type claims it to be. For example, we apply a HTML sniffer for a document |
229 // tagged with text/html here. Whenever this check becomes true, we'll block | 231 // tagged with text/html here. Whenever this check becomes true, we'll block |
230 // the response. | 232 // the response. |
231 if (resp_data->canonical_mime_type != SiteIsolationResponseMetaData::Plain) { | 233 if (resp_data->canonical_mime_type != SiteIsolationResponseMetaData::Plain) { |
232 std::string bucket_prefix; | 234 std::string bucket_prefix; |
233 bool sniffed_as_target_document = false; | 235 bool sniffed_as_target_document = false; |
234 if (resp_data->canonical_mime_type == SiteIsolationResponseMetaData::HTML) { | 236 if (resp_data->canonical_mime_type == SiteIsolationResponseMetaData::HTML) { |
235 bucket_prefix = "SiteIsolation.XSD.HTML"; | 237 bucket_prefix = "SiteIsolation.XSD.HTML"; |
236 sniffed_as_target_document = SniffForHTML(data); | 238 sniffed_as_target_document = |
239 CrossSiteDocumentClassifier::SniffForHTML(data); | |
237 } else if (resp_data->canonical_mime_type == | 240 } else if (resp_data->canonical_mime_type == |
238 SiteIsolationResponseMetaData::XML) { | 241 SiteIsolationResponseMetaData::XML) { |
239 bucket_prefix = "SiteIsolation.XSD.XML"; | 242 bucket_prefix = "SiteIsolation.XSD.XML"; |
240 sniffed_as_target_document = SniffForXML(data); | 243 sniffed_as_target_document = |
244 CrossSiteDocumentClassifier::SniffForXML(data); | |
241 } else if (resp_data->canonical_mime_type == | 245 } else if (resp_data->canonical_mime_type == |
242 SiteIsolationResponseMetaData::JSON) { | 246 SiteIsolationResponseMetaData::JSON) { |
243 bucket_prefix = "SiteIsolation.XSD.JSON"; | 247 bucket_prefix = "SiteIsolation.XSD.JSON"; |
244 sniffed_as_target_document = SniffForJSON(data); | 248 sniffed_as_target_document = |
249 CrossSiteDocumentClassifier::SniffForJSON(data); | |
245 } else { | 250 } else { |
246 NOTREACHED() << "Not a blockable mime type: " | 251 NOTREACHED() << "Not a blockable mime type: " |
247 << resp_data->canonical_mime_type; | 252 << resp_data->canonical_mime_type; |
248 } | 253 } |
249 | 254 |
250 if (sniffed_as_target_document) { | 255 if (sniffed_as_target_document) { |
251 would_block = true; | 256 would_block = true; |
252 HistogramCountBlockedResponse(bucket_prefix, resp_data, false); | 257 HistogramCountBlockedResponse(bucket_prefix, resp_data, false); |
253 } else { | 258 } else { |
254 if (resp_data->no_sniff) { | 259 if (resp_data->no_sniff) { |
255 would_block = true; | 260 would_block = true; |
256 HistogramCountBlockedResponse(bucket_prefix, resp_data, true); | 261 HistogramCountBlockedResponse(bucket_prefix, resp_data, true); |
257 } else { | 262 } else { |
258 HistogramCountNotBlockedResponse(bucket_prefix, sniffed_as_js); | 263 HistogramCountNotBlockedResponse(bucket_prefix, sniffed_as_js); |
259 } | 264 } |
260 } | 265 } |
261 } else { | 266 } else { |
262 // This block is for plain text documents. We apply our HTML, XML, | 267 // This block is for plain text documents. We apply our HTML, XML, |
263 // and JSON sniffer to a text document in the order, and block it | 268 // and JSON sniffer to a text document in the order, and block it |
264 // if any of them succeeds in sniffing. | 269 // if any of them succeeds in sniffing. |
265 std::string bucket_prefix; | 270 std::string bucket_prefix; |
266 if (SniffForHTML(data)) | 271 if (CrossSiteDocumentClassifier::SniffForHTML(data)) |
267 bucket_prefix = "SiteIsolation.XSD.Plain.HTML"; | 272 bucket_prefix = "SiteIsolation.XSD.Plain.HTML"; |
268 else if (SniffForXML(data)) | 273 else if (CrossSiteDocumentClassifier::SniffForXML(data)) |
269 bucket_prefix = "SiteIsolation.XSD.Plain.XML"; | 274 bucket_prefix = "SiteIsolation.XSD.Plain.XML"; |
270 else if (SniffForJSON(data)) | 275 else if (CrossSiteDocumentClassifier::SniffForJSON(data)) |
271 bucket_prefix = "SiteIsolation.XSD.Plain.JSON"; | 276 bucket_prefix = "SiteIsolation.XSD.Plain.JSON"; |
272 | 277 |
273 if (bucket_prefix.size() > 0) { | 278 if (bucket_prefix.size() > 0) { |
274 would_block = true; | 279 would_block = true; |
275 HistogramCountBlockedResponse(bucket_prefix, resp_data, false); | 280 HistogramCountBlockedResponse(bucket_prefix, resp_data, false); |
276 } else if (resp_data->no_sniff) { | 281 } else if (resp_data->no_sniff) { |
277 would_block = true; | 282 would_block = true; |
278 HistogramCountBlockedResponse("SiteIsolation.XSD.Plain", resp_data, true); | 283 HistogramCountBlockedResponse("SiteIsolation.XSD.Plain", resp_data, true); |
279 } else { | 284 } else { |
280 HistogramCountNotBlockedResponse("SiteIsolation.XSD.Plain", | 285 HistogramCountNotBlockedResponse("SiteIsolation.XSD.Plain", |
281 sniffed_as_js); | 286 sniffed_as_js); |
282 } | 287 } |
283 } | 288 } |
284 | 289 |
285 return would_block; | 290 return would_block; |
286 } | 291 } |
287 | 292 |
288 SiteIsolationResponseMetaData::CanonicalMimeType | 293 SiteIsolationResponseMetaData::CanonicalMimeType |
289 SiteIsolationPolicy::GetCanonicalMimeType(const std::string& mime_type) { | 294 CrossSiteDocumentClassifier::GetCanonicalMimeType( |
295 const std::string& mime_type) { | |
290 if (LowerCaseEqualsASCII(mime_type, kTextHtml)) { | 296 if (LowerCaseEqualsASCII(mime_type, kTextHtml)) { |
291 return SiteIsolationResponseMetaData::HTML; | 297 return SiteIsolationResponseMetaData::HTML; |
292 } | 298 } |
293 | 299 |
294 if (LowerCaseEqualsASCII(mime_type, kTextPlain)) { | 300 if (LowerCaseEqualsASCII(mime_type, kTextPlain)) { |
295 return SiteIsolationResponseMetaData::Plain; | 301 return SiteIsolationResponseMetaData::Plain; |
296 } | 302 } |
297 | 303 |
298 if (LowerCaseEqualsASCII(mime_type, kAppJson) || | 304 if (LowerCaseEqualsASCII(mime_type, kAppJson) || |
299 LowerCaseEqualsASCII(mime_type, kTextJson) || | 305 LowerCaseEqualsASCII(mime_type, kTextJson) || |
300 LowerCaseEqualsASCII(mime_type, kTextXjson)) { | 306 LowerCaseEqualsASCII(mime_type, kTextXjson)) { |
301 return SiteIsolationResponseMetaData::JSON; | 307 return SiteIsolationResponseMetaData::JSON; |
302 } | 308 } |
303 | 309 |
304 if (LowerCaseEqualsASCII(mime_type, kTextXml) || | 310 if (LowerCaseEqualsASCII(mime_type, kTextXml) || |
305 LowerCaseEqualsASCII(mime_type, xAppRssXml) || | 311 LowerCaseEqualsASCII(mime_type, xAppRssXml) || |
306 LowerCaseEqualsASCII(mime_type, kAppXml)) { | 312 LowerCaseEqualsASCII(mime_type, kAppXml)) { |
307 return SiteIsolationResponseMetaData::XML; | 313 return SiteIsolationResponseMetaData::XML; |
308 } | 314 } |
309 | 315 |
310 return SiteIsolationResponseMetaData::Others; | 316 return SiteIsolationResponseMetaData::Others; |
311 } | 317 } |
312 | 318 |
313 bool SiteIsolationPolicy::IsBlockableScheme(const GURL& url) { | 319 bool CrossSiteDocumentClassifier::IsBlockableScheme(const GURL& url) { |
314 // We exclude ftp:// from here. FTP doesn't provide a Content-Type | 320 // We exclude ftp:// from here. FTP doesn't provide a Content-Type |
315 // header which our policy depends on, so we cannot protect any | 321 // header which our policy depends on, so we cannot protect any |
316 // document from FTP servers. | 322 // document from FTP servers. |
317 return url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme); | 323 return url.SchemeIs(url::kHttpScheme) || url.SchemeIs(url::kHttpsScheme); |
318 } | 324 } |
319 | 325 |
320 bool SiteIsolationPolicy::IsSameSite(const GURL& frame_origin, | 326 bool CrossSiteDocumentClassifier::IsSameSite(const GURL& frame_origin, |
321 const GURL& response_url) { | 327 const GURL& response_url) { |
322 if (!frame_origin.is_valid() || !response_url.is_valid()) | 328 if (!frame_origin.is_valid() || !response_url.is_valid()) |
323 return false; | 329 return false; |
324 | 330 |
325 if (frame_origin.scheme() != response_url.scheme()) | 331 if (frame_origin.scheme() != response_url.scheme()) |
326 return false; | 332 return false; |
327 | 333 |
328 // SameDomainOrHost() extracts the effective domains (public suffix plus one) | 334 // SameDomainOrHost() extracts the effective domains (public suffix plus one) |
329 // from the two URLs and compare them. | 335 // from the two URLs and compare them. |
330 return net::registry_controlled_domains::SameDomainOrHost( | 336 return net::registry_controlled_domains::SameDomainOrHost( |
331 frame_origin, response_url, | 337 frame_origin, response_url, |
332 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); | 338 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); |
333 } | 339 } |
334 | 340 |
335 // We don't use Webkit's existing CORS policy implementation since | 341 // We don't use Webkit's existing CORS policy implementation since |
336 // their policy works in terms of origins, not sites. For example, | 342 // their policy works in terms of origins, not sites. For example, |
337 // when frame is sub.a.com and it is not allowed to access a document | 343 // when frame is sub.a.com and it is not allowed to access a document |
338 // with sub1.a.com. But under Site Isolation, it's allowed. | 344 // with sub1.a.com. But under Site Isolation, it's allowed. |
339 bool SiteIsolationPolicy::IsValidCorsHeaderSet( | 345 bool CrossSiteDocumentClassifier::IsValidCorsHeaderSet( |
340 const GURL& frame_origin, | 346 const GURL& frame_origin, |
341 const GURL& website_origin, | 347 const GURL& website_origin, |
342 const std::string& access_control_origin) { | 348 const std::string& access_control_origin) { |
343 // Many websites are sending back "\"*\"" instead of "*". This is | 349 // Many websites are sending back "\"*\"" instead of "*". This is |
344 // non-standard practice, and not supported by Chrome. Refer to | 350 // non-standard practice, and not supported by Chrome. Refer to |
345 // CrossOriginAccessControl::passesAccessControlCheck(). | 351 // CrossOriginAccessControl::passesAccessControlCheck(). |
346 | 352 |
347 // TODO(dsjang): * is not allowed for the response from a request | 353 // TODO(dsjang): * is not allowed for the response from a request |
348 // with cookies. This allows for more than what the renderer will | 354 // with cookies. This allows for more than what the renderer will |
349 // eventually be able to receive, so we won't see illegal cross-site | 355 // eventually be able to receive, so we won't see illegal cross-site |
350 // documents allowed by this. We have to find a way to see if this | 356 // documents allowed by this. We have to find a way to see if this |
351 // response is from a cookie-tagged request or not in the future. | 357 // response is from a cookie-tagged request or not in the future. |
352 if (access_control_origin == "*") | 358 if (access_control_origin == "*") |
353 return true; | 359 return true; |
354 | 360 |
355 // TODO(dsjang): The CORS spec only treats a fully specified URL, except for | 361 // TODO(dsjang): The CORS spec only treats a fully specified URL, except for |
356 // "*", but many websites are using just a domain for access_control_origin, | 362 // "*", but many websites are using just a domain for access_control_origin, |
357 // and this is blocked by Webkit's CORS logic here : | 363 // and this is blocked by Webkit's CORS logic here : |
358 // CrossOriginAccessControl::passesAccessControlCheck(). GURL is set | 364 // CrossOriginAccessControl::passesAccessControlCheck(). GURL is set |
359 // is_valid() to false when it is created from a URL containing * in the | 365 // is_valid() to false when it is created from a URL containing * in the |
360 // domain part. | 366 // domain part. |
361 | 367 |
362 GURL cors_origin(access_control_origin); | 368 GURL cors_origin(access_control_origin); |
363 return IsSameSite(frame_origin, cors_origin); | 369 return IsSameSite(frame_origin, cors_origin); |
364 } | 370 } |
365 | 371 |
366 // This function is a slight modification of |net::SniffForHTML|. | 372 // This function is a slight modification of |net::SniffForHTML|. |
367 bool SiteIsolationPolicy::SniffForHTML(StringPiece data) { | 373 bool CrossSiteDocumentClassifier::SniffForHTML(StringPiece data) { |
368 // The content sniffer used by Chrome and Firefox are using "<!--" | 374 // The content sniffer used by Chrome and Firefox are using "<!--" |
369 // as one of the HTML signatures, but it also appears in valid | 375 // as one of the HTML signatures, but it also appears in valid |
370 // JavaScript, considered as well-formed JS by the browser. Since | 376 // JavaScript, considered as well-formed JS by the browser. Since |
371 // we do not want to block any JS, we exclude it from our HTML | 377 // we do not want to block any JS, we exclude it from our HTML |
372 // signatures. This can weaken our document block policy, but we can | 378 // signatures. This can weaken our document block policy, but we can |
373 // break less websites. | 379 // break less websites. |
374 // TODO(dsjang): parameterize |net::SniffForHTML| with an option | 380 // TODO(dsjang): parameterize |net::SniffForHTML| with an option |
375 // that decides whether to include <!-- or not, so that we can | 381 // that decides whether to include <!-- or not, so that we can |
376 // remove this function. | 382 // remove this function. |
377 // TODO(dsjang): Once SiteIsolationPolicy is moved into the browser | 383 // TODO(dsjang): Once CrossSiteDocumentClassifier is moved into the browser |
378 // process, we should do single-thread checking here for the static | 384 // process, we should do single-thread checking here for the static |
379 // initializer. | 385 // initializer. |
380 static const StringPiece kHtmlSignatures[] = { | 386 static const StringPiece kHtmlSignatures[] = { |
381 StringPiece("<!DOCTYPE html"), // HTML5 spec | 387 StringPiece("<!DOCTYPE html"), // HTML5 spec |
382 StringPiece("<script"), // HTML5 spec, Mozilla | 388 StringPiece("<script"), // HTML5 spec, Mozilla |
383 StringPiece("<html"), // HTML5 spec, Mozilla | 389 StringPiece("<html"), // HTML5 spec, Mozilla |
384 StringPiece("<head"), // HTML5 spec, Mozilla | 390 StringPiece("<head"), // HTML5 spec, Mozilla |
385 StringPiece("<iframe"), // Mozilla | 391 StringPiece("<iframe"), // Mozilla |
386 StringPiece("<h1"), // Mozilla | 392 StringPiece("<h1"), // Mozilla |
387 StringPiece("<div"), // Mozilla | 393 StringPiece("<div"), // Mozilla |
(...skipping 25 matching lines...) Expand all Loading... | |
413 if (offset == base::StringPiece::npos) | 419 if (offset == base::StringPiece::npos) |
414 break; | 420 break; |
415 | 421 |
416 // Proceed to the index next to the ending comment (-->). | 422 // Proceed to the index next to the ending comment (-->). |
417 data.remove_prefix(offset + strlen(kEndComment)); | 423 data.remove_prefix(offset + strlen(kEndComment)); |
418 } | 424 } |
419 | 425 |
420 return false; | 426 return false; |
421 } | 427 } |
422 | 428 |
423 bool SiteIsolationPolicy::SniffForXML(base::StringPiece data) { | 429 bool CrossSiteDocumentClassifier::SniffForXML(base::StringPiece data) { |
424 // TODO(dsjang): Chrome's mime_sniffer is using strncasecmp() for | 430 // TODO(dsjang): Chrome's mime_sniffer is using strncasecmp() for |
425 // this signature. However, XML is case-sensitive. Don't we have to | 431 // this signature. However, XML is case-sensitive. Don't we have to |
426 // be more lenient only to block documents starting with the exact | 432 // be more lenient only to block documents starting with the exact |
427 // string <?xml rather than <?XML ? | 433 // string <?xml rather than <?XML ? |
428 // TODO(dsjang): Once SiteIsolationPolicy is moved into the browser | 434 // TODO(dsjang): Once CrossSiteDocumentClassifier is moved into the browser |
429 // process, we should do single-thread checking here for the static | 435 // process, we should do single-thread checking here for the static |
430 // initializer. | 436 // initializer. |
431 static const StringPiece kXmlSignatures[] = {StringPiece("<?xml")}; | 437 static const StringPiece kXmlSignatures[] = {StringPiece("<?xml")}; |
432 return MatchesSignature(data, kXmlSignatures, arraysize(kXmlSignatures)); | 438 return MatchesSignature(data, kXmlSignatures, arraysize(kXmlSignatures)); |
433 } | 439 } |
434 | 440 |
435 bool SiteIsolationPolicy::SniffForJSON(base::StringPiece data) { | 441 bool CrossSiteDocumentClassifier::SniffForJSON(base::StringPiece data) { |
436 // TODO(dsjang): We have to come up with a better way to sniff | 442 // TODO(dsjang): We have to come up with a better way to sniff |
437 // JSON. However, even RE cannot help us that much due to the fact | 443 // JSON. However, even RE cannot help us that much due to the fact |
438 // that we don't do full parsing. This DFA starts with state 0, and | 444 // that we don't do full parsing. This DFA starts with state 0, and |
439 // finds {, "/' and : in that order. We're avoiding adding a | 445 // finds {, "/' and : in that order. We're avoiding adding a |
440 // dependency on a regular expression library. | 446 // dependency on a regular expression library. |
441 enum { | 447 enum { |
442 kStartState, | 448 kStartState, |
443 kLeftBraceState, | 449 kLeftBraceState, |
444 kLeftQuoteState, | 450 kLeftQuoteState, |
445 kColonState, | 451 kColonState, |
(...skipping 25 matching lines...) Expand all Loading... | |
471 break; | 477 break; |
472 case kColonState: | 478 case kColonState: |
473 case kTerminalState: | 479 case kTerminalState: |
474 NOTREACHED(); | 480 NOTREACHED(); |
475 break; | 481 break; |
476 } | 482 } |
477 } | 483 } |
478 return state == kColonState; | 484 return state == kColonState; |
479 } | 485 } |
480 | 486 |
481 bool SiteIsolationPolicy::SniffForJS(StringPiece data) { | 487 bool SiteIsolationStatsGatherer::SniffForJS(StringPiece data) { |
482 // TODO(dsjang): This is a real hack. The only purpose of this function is to | 488 // The purpose of this function is to try to see if there's any possibility |
483 // try to see if there's any possibility that this data can be JavaScript | 489 // that this data can be JavaScript (superset of JS). Search for "var " for JS |
484 // (superset of JS). This function will be removed once UMA stats are | 490 // detection. This is a real hack and should only be used for stats gathering. |
alexmos
2015/06/11 20:17:47
Wow, real hack indeed!
| |
485 // gathered. | |
486 | |
487 // Search for "var " for JS detection. | |
488 return data.find("var ") != base::StringPiece::npos; | 491 return data.find("var ") != base::StringPiece::npos; |
489 } | 492 } |
490 | 493 |
491 } // namespace content | 494 } // namespace content |
OLD | NEW |