diff options
author | japhet@chromium.org <japhet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-04 18:30:09 +0000 |
---|---|---|
committer | japhet@chromium.org <japhet@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-04 18:30:09 +0000 |
commit | 85cc78c5489a2925214a02aef4baf716d74bb1cc (patch) | |
tree | a9945e2369606d83d2977e94636fa35a8f45a765 | |
parent | ae90f252d04c941f8ff9e7a7cfdd20b64c98cd83 (diff) | |
download | chromium_src-85cc78c5489a2925214a02aef4baf716d74bb1cc.zip chromium_src-85cc78c5489a2925214a02aef4baf716d74bb1cc.tar.gz chromium_src-85cc78c5489a2925214a02aef4baf716d74bb1cc.tar.bz2 |
Site isolation metrics: Sniff the actual type of cross origin text/html
responses, and take access control headers into account.
Also, moved the actual logic of site isolation metrics into a separate file.
BUG=none
TEST=none
Review URL: http://codereview.chromium.org/1699028
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46370 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/renderer/render_view.cc | 80 | ||||
-rw-r--r-- | webkit/glue/site_isolation_metrics.cc | 176 | ||||
-rw-r--r-- | webkit/glue/site_isolation_metrics.h | 39 | ||||
-rw-r--r-- | webkit/glue/webkit_glue.gypi | 2 | ||||
-rw-r--r-- | webkit/glue/weburlloader_impl.cc | 12 |
5 files changed, 238 insertions, 71 deletions
diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index f6816f7..655dada 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -116,12 +116,13 @@ #include "webkit/glue/image_decoder.h" #include "webkit/glue/media/buffered_data_source.h" #include "webkit/glue/media/simple_data_source.h" +#include "webkit/glue/media/video_renderer_impl.h" #include "webkit/glue/password_form.h" #include "webkit/glue/plugins/plugin_list.h" #include "webkit/glue/plugins/webplugin_delegate.h" #include "webkit/glue/plugins/webplugin_delegate_impl.h" #include "webkit/glue/plugins/webplugin_impl.h" -#include "webkit/glue/media/video_renderer_impl.h" +#include "webkit/glue/site_isolation_metrics.h" #include "webkit/glue/webdropdata.h" #include "webkit/glue/webkit_glue.h" #include "webkit/glue/webmediaplayer_impl.h" @@ -145,6 +146,7 @@ using webkit_glue::FormField; using webkit_glue::ImageResourceFetcher; using webkit_glue::PasswordForm; using webkit_glue::PasswordFormDomManager; +using webkit_glue::SiteIsolationMetrics; using WebKit::WebAccessibilityCache; using WebKit::WebAccessibilityObject; using WebKit::WebApplicationCacheHost; @@ -2787,74 +2789,6 @@ void RenderView::assignIdentifierToRequest( // Ignore } -// Used in logMimeTypeForCrossOriginRequest(), remove when that function -// is removed -typedef base::hash_map<unsigned, WebURLRequest::TargetType> TargetTypeMap; -static TargetTypeMap target_type_map_; -typedef base::hash_map<std::string, int> MimeTypeMap; -static MimeTypeMap mime_type_map_; - -// Copied from net/base/mime_util.cc, supported_non_image_types[] -static const char* const cross_origin_mime_types_to_log[] = { - "text/cache-manifest", - "text/html", - "text/xml", - "text/xsl", - "text/plain", - "text/vnd.chromium.ftp-dir", - "text/", - "text/css", - "image/svg+xml", - "application/xml", - "application/xhtml+xml", - "application/rss+xml", - "application/atom+xml", - "application/json", - "application/x-x509-user-cert", - "multipart/x-mixed-replace" -}; - -static void initMimeTypeMapIfNeeded() { - if (!mime_type_map_.size()) { - for (size_t i = 0; i < arraysize(cross_origin_mime_types_to_log); ++i) - mime_type_map_[cross_origin_mime_types_to_log[i]] = i; - } -} - -static void logMimeTypeForCrossOriginRequest( - WebFrame* frame, unsigned identifier, const WebURLResponse& response) { - initMimeTypeMapIfNeeded(); - - // Metrics to check the feasability of blocking cross-site requests - // a renderer shouldn't be making (in case we try to move cross-site frames - // into their own process someday). We're erring on the side of counting more - // mime-types then we strictly need (we'd only consider blocking cross-site - // requests with types similar to HTML, XML, or JSON). - // TODO(japhet): Make these more granular. We're ignoring all miscellaneous - // subresource requests, not just the XHRs that might be allowed. - // Also, we should make these metrics be based on something more accurate - // than the mime type header, such as parsing or content sniffing. - TargetTypeMap::iterator iter = target_type_map_.find(identifier); - if (iter != target_type_map_.end()) { - WebURLRequest::TargetType target_type = iter->second; - target_type_map_.erase(iter); - if (target_type != WebURLRequest::TargetIsMainFrame - && target_type != WebURLRequest::TargetIsSubFrame - && target_type != WebURLRequest::TargetIsSubResource - && target_type != WebURLRequest::TargetIsObject - && !frame->securityOrigin().canAccess( - WebSecurityOrigin::create(response.url()))) { - std::string mime_type = response.mimeType().utf8(); - MimeTypeMap::iterator mime_type_iter = mime_type_map_.find(mime_type); - if (mime_type_iter != mime_type_map_.end()) { - UMA_HISTOGRAM_ENUMERATION( - "SiteIsolation.CrossSiteNonFrameResponse_MIME_Type", - mime_type_iter->second, arraysize(cross_origin_mime_types_to_log)); - } - } - } -} - void RenderView::willSendRequest( WebFrame* frame, unsigned identifier, WebURLRequest& request, const WebURLResponse& redirect_response) { @@ -2873,13 +2807,17 @@ void RenderView::willSendRequest( if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kNoReferrers)) request.clearHTTPHeaderField("Referer"); - target_type_map_[identifier] = request.targetType(); + // Temporary metrics, see site_isolation_metrics.h + SiteIsolationMetrics::AddRequest(identifier, request.targetType()); } void RenderView::didReceiveResponse( WebFrame* frame, unsigned identifier, const WebURLResponse& response) { - logMimeTypeForCrossOriginRequest(frame, identifier, response); + // Temporary metrics, see site_isolation_metrics.h + SiteIsolationMetrics::LogMimeTypeForCrossOriginRequest(frame, + identifier, + response); // Only do this for responses that correspond to a provisional data source // of the top-most frame. If we have a provisional data source, then we diff --git a/webkit/glue/site_isolation_metrics.cc b/webkit/glue/site_isolation_metrics.cc new file mode 100644 index 0000000..a97bb96 --- /dev/null +++ b/webkit/glue/site_isolation_metrics.cc @@ -0,0 +1,176 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/glue/site_isolation_metrics.h" + +#include <set> + +#include "base/hash_tables.h" +#include "base/histogram.h" +#include "net/base/mime_sniffer.h" +#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/WebKit/chromium/public/WebSecurityOrigin.h" +#include "third_party/WebKit/WebKit/chromium/public/WebString.h" +#include "third_party/WebKit/WebKit/chromium/public/WebURL.h" +#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h" +#include "third_party/WebKit/WebKit/chromium/public/WebURLResponse.h" + +using WebKit::WebFrame; +using WebKit::WebSecurityOrigin; +using WebKit::WebString; +using WebKit::WebURL; +using WebKit::WebURLRequest; +using WebKit::WebURLResponse; + +namespace webkit_glue { + +typedef base::hash_map<unsigned, WebURLRequest::TargetType> TargetTypeMap; +typedef base::hash_map<std::string, int> MimeTypeMap; +typedef std::set<std::string> CrossOriginTextHtmlResponseSet; + +static TargetTypeMap* GetTargetTypeMap() { + static TargetTypeMap target_type_map_; + return &target_type_map_; +} + +// Copied from net/base/mime_util.cc, supported_non_image_types[] +static const char* const kCrossOriginMimeTypesToLog[] = { + "text/cache-manifest", + "text/html", + "text/xml", + "text/xsl", + "text/plain", + "text/vnd.chromium.ftp-dir", + "text/", + "text/css", + "image/svg+xml", + "application/xml", + "application/xhtml+xml", + "application/rss+xml", + "application/atom+xml", + "application/json", + "application/x-x509-user-cert", + "multipart/x-mixed-replace" +}; + +static MimeTypeMap* GetMimeTypeMap() { + static MimeTypeMap mime_type_map_; + if (!mime_type_map_.size()) { + for (size_t i = 0; i < arraysize(kCrossOriginMimeTypesToLog); ++i) + mime_type_map_[kCrossOriginMimeTypesToLog[i]] = i; + } + return &mime_type_map_; +} + +// This is set is used to keep track of the response urls that we want to +// sniff, since we will have to wait for the payload to arrive. +static CrossOriginTextHtmlResponseSet* GetCrossOriginTextHtmlResponseSet() { + static CrossOriginTextHtmlResponseSet cross_origin_text_html_response_set_; + return &cross_origin_text_html_response_set_; +} + +static void LogVerifiedTextHtmlResponse() { + UMA_HISTOGRAM_COUNTS( + "SiteIsolation.CrossSiteNonFrameResponse_verified_texthtml", 1); +} + +static void LogMislabeledTextHtmlResponse() { + UMA_HISTOGRAM_COUNTS( + "SiteIsolation.CrossSiteNonFrameResponse_mislabeled_texthtml", 1); +} + +void SiteIsolationMetrics::AddRequest(unsigned identifier, + WebURLRequest::TargetType target_type) { + TargetTypeMap& target_type_map = *GetTargetTypeMap(); + target_type_map[identifier] = target_type; +} + +void SiteIsolationMetrics::LogMimeTypeForCrossOriginRequest( + WebFrame* frame, unsigned identifier, const WebURLResponse& response) { + TargetTypeMap& target_type_map = *GetTargetTypeMap(); + TargetTypeMap::iterator iter = target_type_map.find(identifier); + if (iter != target_type_map.end()) { + WebURLRequest::TargetType target_type = iter->second; + target_type_map.erase(iter); + // We want to log any cross-site request that we don't think a renderer + // should be allowed to make. We can safely ignore frame requests (since + // we'd like those to be in a separate renderer) and plugin requests, even + // if they are cross-origin. + if (target_type != WebURLRequest::TargetIsMainFrame && + target_type != WebURLRequest::TargetIsSubFrame && + target_type != WebURLRequest::TargetIsObject && + !frame->securityOrigin().canAccess( + WebSecurityOrigin::create(response.url()))) { + std::string mime_type = response.mimeType().utf8(); + MimeTypeMap mime_type_map = *GetMimeTypeMap(); + MimeTypeMap::iterator mime_type_iter = mime_type_map.find(mime_type); + if (mime_type_iter != mime_type_map.end()) { + UMA_HISTOGRAM_ENUMERATION( + "SiteIsolation.CrossSiteNonFrameResponse_MIME_Type", + mime_type_iter->second, + arraysize(kCrossOriginMimeTypesToLog)); + + // We also should check access control headers, in case this + // cross-origin request has been explicitly permitted. + // This is basically a copy of the logic of passesAccessControlCheck() + // in WebCore/loader/CrossOriginAccessControl.cpp. + WebString access_control_origin = response.httpHeaderField( + WebString::fromUTF8("Access-Control-Allow-Origin")); + if (access_control_origin != WebString::fromUTF8("*") + && !frame->securityOrigin().canAccess( + WebSecurityOrigin::createFromString(access_control_origin))) { + UMA_HISTOGRAM_ENUMERATION( + "SiteIsolation.CrossSiteNonFrameResponse_With_CORS_MIME_Type", + mime_type_iter->second, + arraysize(kCrossOriginMimeTypesToLog)); + } + + // Sometimes resources are mislabeled as text/html. Once we have some + // of the payload, we want to determine if this was actually text/html. + if (mime_type == "text/html") + GetCrossOriginTextHtmlResponseSet()->insert(response.url().spec()); + } + } + } +} + +void SiteIsolationMetrics::SniffCrossOriginHTML(const WebURL& response_url, + const char* data, + int len) { + if (!response_url.isValid()) + return; + + CrossOriginTextHtmlResponseSet& cross_origin_text_html_response_set = + *GetCrossOriginTextHtmlResponseSet(); + CrossOriginTextHtmlResponseSet::iterator request_iter = + cross_origin_text_html_response_set.find(response_url.spec()); + if (request_iter != cross_origin_text_html_response_set.end()) { + std::string sniffed_mime_type; + bool successful = net::SniffMimeType(data, len, response_url, + "", &sniffed_mime_type); + if (successful && sniffed_mime_type == "text/html") + LogVerifiedTextHtmlResponse(); + else + LogMislabeledTextHtmlResponse(); + cross_origin_text_html_response_set.erase(request_iter); + } +} + +void SiteIsolationMetrics::RemoveCompletedResponse( + const WebURL& response_url) { + if (!response_url.isValid()) + return; + + // Ensure we don't leave responses in the set after they've completed. + CrossOriginTextHtmlResponseSet& cross_origin_text_html_response_set = + *GetCrossOriginTextHtmlResponseSet(); + CrossOriginTextHtmlResponseSet::iterator request_iter = + cross_origin_text_html_response_set.find(response_url.spec()); + if (request_iter != cross_origin_text_html_response_set.end()) { + LogMislabeledTextHtmlResponse(); + cross_origin_text_html_response_set.erase(request_iter); + } +} + +} // namespace webkit_glue diff --git a/webkit/glue/site_isolation_metrics.h b/webkit/glue/site_isolation_metrics.h new file mode 100644 index 0000000..1148afb --- /dev/null +++ b/webkit/glue/site_isolation_metrics.h @@ -0,0 +1,39 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_GLUE_SITE_ISOLATION_METRICS_H_ +#define WEBKIT_GLUE_SITE_ISOLATION_METRICS_H_ + +#include "third_party/WebKit/WebKit/chromium/public/WebURLRequest.h" + +namespace WebKit { +class WebFrame; +class WebURL; +class WebURLResponse; +} + +namespace webkit_glue { + +// Metrics to check the feasability of blocking cross-site requests +// a renderer shouldn't be making (in case we try to move cross-site frames +// into their own process someday). We're erring on the side of counting more +// mime-types then we strictly need (we'd only consider blocking cross-site +// requests with types similar to HTML, XML, or JSON). +class SiteIsolationMetrics { + public: + static void AddRequest(unsigned identifier, + WebKit::WebURLRequest::TargetType target_type); + static void LogMimeTypeForCrossOriginRequest( + WebKit::WebFrame* frame, + unsigned identifier, + const WebKit::WebURLResponse& response); + static void SniffCrossOriginHTML(const WebKit::WebURL& response_url, + const char* data, + int len); + static void RemoveCompletedResponse(const WebKit::WebURL& response_url); +}; + +} // namespace webkit_glue + +#endif // WEBKIT_GLUE_SITE_ISOLATION_METRICS_H_ diff --git a/webkit/glue/webkit_glue.gypi b/webkit/glue/webkit_glue.gypi index 00a2c81..5ea5e21 100644 --- a/webkit/glue/webkit_glue.gypi +++ b/webkit/glue/webkit_glue.gypi @@ -238,6 +238,8 @@ 'scoped_clipboard_writer_glue.h', 'simple_webmimeregistry_impl.cc', 'simple_webmimeregistry_impl.h', + 'site_isolation_metrics.cc', + 'site_isolation_metrics.h', 'webaccessibility.cc', 'webaccessibility.h', 'webclipboard_impl.cc', diff --git a/webkit/glue/weburlloader_impl.cc b/webkit/glue/weburlloader_impl.cc index 710a84b..0b5697a 100644 --- a/webkit/glue/weburlloader_impl.cc +++ b/webkit/glue/weburlloader_impl.cc @@ -26,6 +26,7 @@ #include "webkit/glue/ftp_directory_listing_response_delegate.h" #include "webkit/glue/multipart_response_delegate.h" #include "webkit/glue/resource_loader_bridge.h" +#include "webkit/glue/site_isolation_metrics.h" #include "webkit/glue/webkit_glue.h" using base::Time; @@ -249,6 +250,9 @@ class WebURLLoaderImpl::Context : public base::RefCounted<Context>, scoped_ptr<ResourceLoaderBridge> bridge_; scoped_ptr<FtpDirectoryListingResponseDelegate> ftp_listing_delegate_; scoped_ptr<MultipartResponseDelegate> multipart_delegate_; + + // TODO(japhet): Storing this is a temporary hack for site isolation logging. + WebURL response_url_; }; WebURLLoaderImpl::Context::Context(WebURLLoaderImpl* loader) @@ -506,12 +510,17 @@ void WebURLLoaderImpl::Context::OnReceivedResponse( ftp_listing_delegate_.reset( new FtpDirectoryListingResponseDelegate(client_, loader_, response)); } + + response_url_ = response.url(); } void WebURLLoaderImpl::Context::OnReceivedData(const char* data, int len) { if (!client_) return; + // Temporary logging, see site_isolation_metrics.h/cc. + SiteIsolationMetrics::SniffCrossOriginHTML(response_url_, data, len); + if (ftp_listing_delegate_.get()) { // The FTP listing delegate will make the appropriate calls to // client_->didReceiveData and client_->didReceiveResponse. @@ -559,6 +568,9 @@ void WebURLLoaderImpl::Context::OnCompletedRequest( } } + // Temporary logging, see site_isolation_metrics.h/cc + SiteIsolationMetrics::RemoveCompletedResponse(response_url_); + // We are done with the bridge now, and so we need to release the reference // to ourselves that we took on behalf of the bridge. This may cause our // destruction. |