OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. Use |
| 2 // of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #ifndef CONTENT_SITE_ISOLATION_POLICY_H_ |
| 6 #define CONTENT_SITE_ISOLATION_POLICY_H_ |
| 7 |
| 8 #include <map> |
| 9 #include <utility> |
| 10 |
| 11 #include "base/gtest_prod_util.h" |
| 12 #include "content/common/content_export.h" |
| 13 #include "third_party/WebKit/public/platform/WebURLRequest.h" |
| 14 #include "third_party/WebKit/public/platform/WebURLResponse.h" |
| 15 #include "third_party/WebKit/public/web/WebFrame.h" |
| 16 #include "third_party/WebKit/public/web/WebSecurityOrigin.h" |
| 17 #include "webkit/child/resource_loader_bridge.h" |
| 18 #include "webkit/common/resource_response_info.h" |
| 19 |
| 20 namespace content { |
| 21 |
| 22 // SiteIsolationPolicy implements the cross-site document blocking policy (XSDP) |
| 23 // for Site Isolation. XSDP will monitor network responses to a renderer and |
| 24 // block illegal responses so that a compromised renderer cannot steal private |
| 25 // information from other sites. For now SiteIsolationPolicy monitors responses |
| 26 // to gather various UMA stats to see the compatibility impact of actual |
| 27 // deployment of the policy. The UMA stat categories SiteIsolationPolicy gathers |
| 28 // are as follows: |
| 29 // |
| 30 // SiteIsolation.AllResponses : # of all network responses. |
| 31 // SiteIsolation.XSD.DataLength : the length of the first packet of a response. |
| 32 // SiteIsolation.XSD.MimeType (enum): |
| 33 // # of responses from other sites, tagged with a document mime type. |
| 34 // 0:HTML, 1:XML, 2:JSON, 3:Plain, 4:Others |
| 35 // SiteIsolation.XSD.[%MIMETYPE].Blocked : |
| 36 // blocked # of cross-site document responses grouped by sniffed MIME type. |
| 37 // SiteIsolation.XSD.[%MIMETYPE].Blocked.RenderableStatusCode : |
| 38 // # of responses with renderable status code, |
| 39 // out of SiteIsolation.XSD.[%MIMETYPE].Blocked. |
| 40 // SiteIsolation.XSD.[%MIMETYPE].Blocked.NonRenderableStatusCode : |
| 41 // # of responses with non-renderable status code, |
| 42 // out of SiteIsolation.XSD.[%MIMETYPE].Blocked. |
| 43 // SiteIsolation.XSD.[%MIMETYPE].NoSniffBlocked.RenderableStatusCode : |
| 44 // # of responses failed to be sniffed for its MIME type, but blocked by |
| 45 // "X-Content-Type-Options: nosniff" header, and with renderable status code |
| 46 // out of SiteIsolation.XSD.[%MIMETYPE].Blocked. |
| 47 // SiteIsolation.XSD.[%MIMETYPE].NoSniffBlocked.NonRenderableStatusCode : |
| 48 // # of responses failed to be sniffed for its MIME type, but blocked by |
| 49 // "X-Content-Type-Options: nosniff" header, and with non-renderable status |
| 50 // code out of SiteIsolation.XSD.[%MIMETYPE].Blocked. |
| 51 // SiteIsolation.XSD.[%MIMETYPE].NotBlocked : |
| 52 // # of responses, but not blocked due to failure of mime sniffing. |
| 53 // SiteIsolation.XSD.[%MIMETYPE].NotBlocked.MaybeJS : |
| 54 // # of responses that are plausibly sniffed to be JavaScript. |
| 55 |
| 56 class SiteIsolationPolicy |
| 57 : public base::RefCounted<SiteIsolationPolicy>, |
| 58 public webkit_glue::ResourceLoaderBridge::Peer { |
| 59 public: |
| 60 |
| 61 SiteIsolationPolicy( |
| 62 webkit_glue::ResourceLoaderBridge::Peer* original_peer, |
| 63 bool policy_enforced, |
| 64 GURL& frame_origin, |
| 65 GURL& request_url, |
| 66 int request_id, |
| 67 ResourceType::Type resource_type); |
| 68 |
| 69 // ResourceLoaderBridge::Peer methods: we directly calls original_peer_'s |
| 70 // corresponding member functions other than OnReceiveRedirect(), |
| 71 // OnReceivedResponse(), OnReceivedData(), and OnCompletedRequest(). |
| 72 virtual void OnUploadProgress(uint64 position, uint64 size) OVERRIDE; |
| 73 virtual void OnDownloadedData(int len) OVERRIDE; |
| 74 virtual void OnReceivedCachedMetadata(const char* data, int len) OVERRIDE; |
| 75 virtual void OnCompletedRequest( |
| 76 int error_code, |
| 77 bool was_ignored_by_handler, |
| 78 const std::string& security_info, |
| 79 const base::TimeTicks& completion_time) OVERRIDE; |
| 80 |
| 81 // Updates the request_url_ to |new_url| when the request this peer is |
| 82 // listening to is redirected. |
| 83 virtual bool OnReceivedRedirect( |
| 84 const GURL& new_url, |
| 85 const webkit_glue::ResourceResponseInfo& info, |
| 86 bool* has_new_first_party_for_cookies, |
| 87 GURL* new_first_party_for_cookies) OVERRIDE; |
| 88 |
| 89 // Records the HTTP header information of the response. We use the recorded |
| 90 // header information to decide if this response will be the subject of |
| 91 // cross-site document blocking. If it is, cross_site_document_header_ is set |
| 92 // to true. This also makes state_ proceed from INIT to RESPONSE_RECEIVED. |
| 93 virtual void OnReceivedResponse( |
| 94 const webkit_glue::ResourceResponseInfo& info) OVERRIDE; |
| 95 |
| 96 // Examines the first network packet to see whether this response should be |
| 97 // blocked by our cross-site document policy. This only examines response data |
| 98 // when cross_site_document_header_ is true by OnReceivedResponse(). We apply |
| 99 // content-sniffing to the first network packet to decide to block the |
| 100 // response and records various kinds of UMA data stats at the same time. |
| 101 // TODO(dsjang): Here is where we can do actual blocking of the body of a |
| 102 // response. When the body is blocked, we can return an empty string instead. |
| 103 virtual void OnReceivedData(const char* data, |
| 104 int data_length, |
| 105 int encoded_data_length) OVERRIDE; |
| 106 |
| 107 webkit_glue::ResourceLoaderBridge::Peer* get_wrapped_peer() { |
| 108 return original_peer_; |
| 109 } |
| 110 |
| 111 void set_wrapped_peer(webkit_glue::ResourceLoaderBridge::Peer* peer) { |
| 112 original_peer_ = peer; |
| 113 } |
| 114 |
| 115 private: |
| 116 FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, IsBlockableScheme); |
| 117 FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, IsSameSite); |
| 118 FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, IsValidCorsHeaderSet); |
| 119 FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, SniffForHTML); |
| 120 FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, SniffForXML); |
| 121 FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, SniffForJSON); |
| 122 FRIEND_TEST_ALL_PREFIXES(SiteIsolationPolicyTest, SniffForJS); |
| 123 |
| 124 enum CanonicalMimeType { |
| 125 HTML = 0, |
| 126 XML = 1, |
| 127 JSON = 2, |
| 128 Plain = 3, |
| 129 Others = 4, |
| 130 MaxCanonicalMimeType, |
| 131 }; |
| 132 |
| 133 enum RequestProgressState { |
| 134 INIT = 0, |
| 135 RESPONSE_RECEIVED = 1, |
| 136 DATA_RECEIVED = 2, |
| 137 COMPLETED = 3, |
| 138 }; |
| 139 |
| 140 // Returns the representative mime type enum value of the mime type of |
| 141 // response. For example, this returns the same value for all text/xml mime |
| 142 // type families such as application/xml, application/rss+xml. |
| 143 static CanonicalMimeType GetCanonicalMimeType( |
| 144 const std::string& mime_type); |
| 145 |
| 146 // Returns whether this scheme is a target of cross-site document |
| 147 // policy(XSDP). This returns true only for http://* and https://* urls. |
| 148 static bool IsBlockableScheme(const GURL& frame_origin); |
| 149 |
| 150 // Returns whether the two urls belong to the same sites. |
| 151 static bool IsSameSite(const GURL& frame_origin, const GURL& response_url); |
| 152 |
| 153 // Returns whether there's a valid CORS header for frame_origin. This is |
| 154 // simliar to CrossOriginAccessControl::passesAccessControlCheck(), but we use |
| 155 // sites as our security domain, not origins. TODO(dsjang): this must be |
| 156 // improved to be more accurate to the actual CORS specification. For now, |
| 157 // this works conservatively, allowing XSDs that are not allowed by actual |
| 158 // CORS rules by ignoring 1) credentials and 2) methods. Preflight requests |
| 159 // don't matter here since they are not used to decide whether to block a |
| 160 // document or not on the client side. |
| 161 static bool IsValidCorsHeaderSet(GURL& frame_origin, |
| 162 GURL& website_origin, |
| 163 std::string access_control_origin); |
| 164 |
| 165 static bool SniffForHTML(const char* data, size_t length); |
| 166 static bool SniffForXML(const char* data, size_t length); |
| 167 static bool SniffForJSON(const char* data, size_t length); |
| 168 |
| 169 static bool MatchesSignature(const char* data, |
| 170 size_t length, |
| 171 const char* signatures[], |
| 172 size_t arr_size); |
| 173 |
| 174 // TODO(dsjang): this is only needed for collecting UMA stat. Will be deleted |
| 175 // when this class is used for actual blocking. |
| 176 static bool SniffForJS(const char* data, size_t length); |
| 177 |
| 178 // TODO(dsjang): this is only needed for collecting UMA stat. Will be deleted |
| 179 // when this class is used for actual blocking. |
| 180 static bool IsRenderableStatusCodeForDocument(int status_code); |
| 181 |
| 182 friend class base::RefCounted<SiteIsolationPolicy>; |
| 183 virtual ~SiteIsolationPolicy() {} |
| 184 |
| 185 // This class wraps original_peer_ to intercept certain calls of it to monitor |
| 186 // the lifetime of a request to apply our cross-site document blocking. |
| 187 webkit_glue::ResourceLoaderBridge::Peer* original_peer_; |
| 188 |
| 189 // These are fixed when the object is created. Flag that decides that we |
| 190 // actually block a response by enforcing the cross-site document policy. |
| 191 bool policy_enforced_; |
| 192 GURL frame_origin_; |
| 193 // However, request_url_ is updated to a new url when redirected. |
| 194 GURL request_url_; |
| 195 int request_id_; |
| 196 ResourceType::Type resource_type_; |
| 197 |
| 198 // The current state of the request. This can only proceeds to a greater value |
| 199 // when an event happens. This is only used for sanity checks in DCHECK() to |
| 200 // see if callbacks are called in an expected order. |
| 201 RequestProgressState state_; |
| 202 |
| 203 // This is set to true when we decide that the response is a target of our |
| 204 // cross-site document blocking policy, decided when the headers are deceived |
| 205 // by OnReceivedResponse(). |
| 206 bool cross_site_document_header_; |
| 207 |
| 208 // The following 3 member variables are only valid after OnReceivedResponse() |
| 209 // is called (after RESPONSE_RECEIVED). This must be the same as request_url_ |
| 210 // since request_url_ is updated whenever the request is redirected. |
| 211 CanonicalMimeType canonical_mime_type_; |
| 212 int http_status_code_; |
| 213 bool no_sniff_; |
| 214 |
| 215 // This is initially false, but becomes true after we confirm that this is |
| 216 // safe under our cross-site document policy by applying content sniffing to |
| 217 // the first network packet from the response. |
| 218 bool confirmed_safe_; |
| 219 |
| 220 DISALLOW_COPY_AND_ASSIGN(SiteIsolationPolicy); |
| 221 }; |
| 222 |
| 223 } // namespace content |
| 224 |
| 225 #endif // CONTENT_SITE_ISOLATION_POLICY_H_ |
OLD | NEW |