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