• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/base/isolation_info.h"
6 
7 #include <cstddef>
8 #include <optional>
9 
10 #include "base/check_op.h"
11 #include "base/unguessable_token.h"
12 #include "net/base/features.h"
13 #include "net/base/isolation_info.h"
14 #include "net/base/isolation_info.pb.h"
15 #include "net/base/network_anonymization_key.h"
16 #include "net/base/proxy_server.h"
17 
18 namespace net {
19 
20 namespace {
21 
22 // Checks that |origin| is consistent with |site_for_cookies|.
ValidateSameSite(const url::Origin & origin,const SiteForCookies & site_for_cookies)23 bool ValidateSameSite(const url::Origin& origin,
24                       const SiteForCookies& site_for_cookies) {
25   // If not sending SameSite cookies, or sending them for a non-scheme, consider
26   // all origins consistent. Note that SiteForCookies should never be created
27   // for websocket schemes for valid navigations, since frames can't be
28   // navigated to those schemes.
29   if (site_for_cookies.IsNull() ||
30       (site_for_cookies.scheme() != url::kHttpScheme &&
31        site_for_cookies.scheme() != url::kHttpsScheme)) {
32     return true;
33   }
34 
35   // Shouldn't send cookies for opaque origins.
36   if (origin.opaque())
37     return false;
38 
39   // TODO(https://crbug.com/1060631): GetURL() is expensive. Maybe make a
40   // version of IsFirstParty that works on origins?
41   return site_for_cookies.IsFirstParty(origin.GetURL());
42 }
43 
44 // Checks if these values are consistent. See IsolationInfo::Create() for
45 // descriptions of consistent sets of values. Also allows values used by the
46 // 0-argument constructor.
IsConsistent(IsolationInfo::RequestType request_type,const std::optional<url::Origin> & top_frame_origin,const std::optional<url::Origin> & frame_origin,const SiteForCookies & site_for_cookies,const std::optional<base::UnguessableToken> & nonce)47 bool IsConsistent(IsolationInfo::RequestType request_type,
48                   const std::optional<url::Origin>& top_frame_origin,
49                   const std::optional<url::Origin>& frame_origin,
50                   const SiteForCookies& site_for_cookies,
51                   const std::optional<base::UnguessableToken>& nonce) {
52   // Check for the default-constructed case.
53   if (!top_frame_origin) {
54     return request_type == IsolationInfo::RequestType::kOther &&
55            !frame_origin && !nonce && site_for_cookies.IsNull();
56   }
57 
58   // As long as there is a |top_frame_origin|, |site_for_cookies| must be
59   // consistent with the |top_frame_origin|.
60   if (!ValidateSameSite(*top_frame_origin, site_for_cookies))
61     return false;
62 
63   // Validate frame `frame_origin`
64   // IsolationInfo must have a `frame_origin` when frame origins are enabled
65   // and the IsolationInfo is not default-constructed.
66   if (!frame_origin) {
67     return false;
68   }
69   switch (request_type) {
70     case IsolationInfo::RequestType::kMainFrame:
71       // TODO(https://crbug.com/1056706): Check that |top_frame_origin| and
72       // |frame_origin| are the same, once the ViewSource code creates a
73       // consistent IsolationInfo object.
74       //
75       // TODO(https://crbug.com/1060631): Once CreatePartial() is removed,
76       // check if SiteForCookies is non-null if the scheme is HTTP or HTTPS.
77       break;
78     case IsolationInfo::RequestType::kSubFrame:
79       // For subframe navigations, the subframe's origin may not be consistent
80       // with the SiteForCookies, so SameSite cookies may be sent if there's a
81       // redirect to main frames site.
82       break;
83     case IsolationInfo::RequestType::kOther:
84       // SiteForCookies must consistent with the frame origin as well for
85       // subresources.
86       return ValidateSameSite(*frame_origin, site_for_cookies);
87   }
88   return true;
89 }
90 
91 }  // namespace
92 
IsolationInfo()93 IsolationInfo::IsolationInfo()
94     : IsolationInfo(RequestType::kOther,
95                     /*top_frame_origin=*/std::nullopt,
96                     /*frame_origin=*/std::nullopt,
97                     SiteForCookies(),
98                     /*nonce=*/std::nullopt) {}
99 
100 IsolationInfo::IsolationInfo(const IsolationInfo&) = default;
101 IsolationInfo::IsolationInfo(IsolationInfo&&) = default;
102 IsolationInfo::~IsolationInfo() = default;
103 IsolationInfo& IsolationInfo::operator=(const IsolationInfo&) = default;
104 IsolationInfo& IsolationInfo::operator=(IsolationInfo&&) = default;
105 
CreateForInternalRequest(const url::Origin & top_frame_origin)106 IsolationInfo IsolationInfo::CreateForInternalRequest(
107     const url::Origin& top_frame_origin) {
108   return IsolationInfo(RequestType::kOther, top_frame_origin, top_frame_origin,
109                        SiteForCookies::FromOrigin(top_frame_origin),
110                        /*nonce=*/std::nullopt);
111 }
112 
CreateTransient()113 IsolationInfo IsolationInfo::CreateTransient() {
114   url::Origin opaque_origin;
115   return IsolationInfo(RequestType::kOther, opaque_origin, opaque_origin,
116                        SiteForCookies(), /*nonce=*/std::nullopt);
117 }
118 
Deserialize(const std::string & serialized)119 std::optional<IsolationInfo> IsolationInfo::Deserialize(
120     const std::string& serialized) {
121   proto::IsolationInfo proto;
122   if (!proto.ParseFromString(serialized))
123     return std::nullopt;
124 
125   std::optional<url::Origin> top_frame_origin;
126   if (proto.has_top_frame_origin())
127     top_frame_origin = url::Origin::Create(GURL(proto.top_frame_origin()));
128 
129   std::optional<url::Origin> frame_origin;
130   if (proto.has_frame_origin())
131     frame_origin = url::Origin::Create(GURL(proto.frame_origin()));
132 
133   return IsolationInfo::CreateIfConsistent(
134       static_cast<RequestType>(proto.request_type()),
135       std::move(top_frame_origin), std::move(frame_origin),
136       SiteForCookies::FromUrl(GURL(proto.site_for_cookies())),
137       /*nonce=*/std::nullopt);
138 }
139 
Create(RequestType request_type,const url::Origin & top_frame_origin,const url::Origin & frame_origin,const SiteForCookies & site_for_cookies,const std::optional<base::UnguessableToken> & nonce)140 IsolationInfo IsolationInfo::Create(
141     RequestType request_type,
142     const url::Origin& top_frame_origin,
143     const url::Origin& frame_origin,
144     const SiteForCookies& site_for_cookies,
145     const std::optional<base::UnguessableToken>& nonce) {
146   return IsolationInfo(request_type, top_frame_origin, frame_origin,
147                        site_for_cookies, nonce);
148 }
149 
DoNotUseCreatePartialFromNak(const net::NetworkAnonymizationKey & network_anonymization_key)150 IsolationInfo IsolationInfo::DoNotUseCreatePartialFromNak(
151     const net::NetworkAnonymizationKey& network_anonymization_key) {
152   if (!network_anonymization_key.IsFullyPopulated()) {
153     return IsolationInfo();
154   }
155 
156   url::Origin top_frame_origin =
157       network_anonymization_key.GetTopFrameSite()->site_as_origin_;
158 
159   std::optional<url::Origin> frame_origin;
160   if (network_anonymization_key.IsCrossSite()) {
161     // If we know that the origin is cross site to the top level site, create an
162     // empty origin to use as the frame origin for the isolation info. This
163     // should be cross site with the top level origin.
164     frame_origin = url::Origin();
165   } else {
166     // If we don't know that it's cross site to the top level site, use the top
167     // frame site to set the frame origin.
168     frame_origin = top_frame_origin;
169   }
170 
171   const std::optional<base::UnguessableToken>& nonce =
172       network_anonymization_key.GetNonce();
173 
174   auto isolation_info = IsolationInfo::Create(
175       IsolationInfo::RequestType::kOther, top_frame_origin,
176       frame_origin.value(), SiteForCookies(), nonce);
177   // TODO(crbug/1343856): DCHECK isolation info is fully populated.
178   return isolation_info;
179 }
180 
CreateIfConsistent(RequestType request_type,const std::optional<url::Origin> & top_frame_origin,const std::optional<url::Origin> & frame_origin,const SiteForCookies & site_for_cookies,const std::optional<base::UnguessableToken> & nonce)181 std::optional<IsolationInfo> IsolationInfo::CreateIfConsistent(
182     RequestType request_type,
183     const std::optional<url::Origin>& top_frame_origin,
184     const std::optional<url::Origin>& frame_origin,
185     const SiteForCookies& site_for_cookies,
186     const std::optional<base::UnguessableToken>& nonce) {
187   if (!IsConsistent(request_type, top_frame_origin, frame_origin,
188                     site_for_cookies, nonce)) {
189     return std::nullopt;
190   }
191   return IsolationInfo(request_type, top_frame_origin, frame_origin,
192                        site_for_cookies, nonce);
193 }
194 
CreateForRedirect(const url::Origin & new_origin) const195 IsolationInfo IsolationInfo::CreateForRedirect(
196     const url::Origin& new_origin) const {
197   if (request_type_ == RequestType::kOther)
198     return *this;
199 
200   if (request_type_ == RequestType::kSubFrame) {
201     return IsolationInfo(request_type_, top_frame_origin_, new_origin,
202                          site_for_cookies_, nonce_);
203   }
204 
205   DCHECK_EQ(RequestType::kMainFrame, request_type_);
206   return IsolationInfo(request_type_, new_origin, new_origin,
207                        SiteForCookies::FromOrigin(new_origin), nonce_);
208 }
209 
frame_origin() const210 const std::optional<url::Origin>& IsolationInfo::frame_origin() const {
211   return frame_origin_;
212 }
213 
frame_origin_for_testing() const214 const std::optional<url::Origin>& IsolationInfo::frame_origin_for_testing()
215     const {
216   return frame_origin_;
217 }
218 
IsEqualForTesting(const IsolationInfo & other) const219 bool IsolationInfo::IsEqualForTesting(const IsolationInfo& other) const {
220   return (request_type_ == other.request_type_ &&
221           top_frame_origin_ == other.top_frame_origin_ &&
222           frame_origin_ == other.frame_origin_ &&
223           network_isolation_key_ == other.network_isolation_key_ &&
224           network_anonymization_key_ == other.network_anonymization_key_ &&
225           nonce_ == other.nonce_ &&
226           site_for_cookies_.IsEquivalent(other.site_for_cookies_));
227 }
228 
Serialize() const229 std::string IsolationInfo::Serialize() const {
230   if (network_isolation_key().IsTransient())
231     return "";
232 
233   proto::IsolationInfo info;
234 
235   info.set_request_type(static_cast<int32_t>(request_type_));
236 
237   if (top_frame_origin_)
238     info.set_top_frame_origin(top_frame_origin_->Serialize());
239 
240   if (frame_origin_)
241     info.set_frame_origin(frame_origin_->Serialize());
242 
243   info.set_site_for_cookies(site_for_cookies_.RepresentativeUrl().spec());
244 
245   return info.SerializeAsString();
246 }
247 
DebugString() const248 std::string IsolationInfo::DebugString() const {
249   std::string s;
250   s += "request_type: ";
251   switch (request_type_) {
252     case IsolationInfo::RequestType::kMainFrame:
253       s += "kMainFrame";
254       break;
255     case IsolationInfo::RequestType::kSubFrame:
256       s += "kSubFrame";
257       break;
258     case IsolationInfo::RequestType::kOther:
259       s += "kOther";
260       break;
261   }
262 
263   s += "; top_frame_origin: ";
264   if (top_frame_origin_) {
265     s += top_frame_origin_.value().GetDebugString(true);
266   } else {
267     s += "(none)";
268   }
269 
270   s += "; frame_origin: ";
271   if (frame_origin_) {
272     s += frame_origin_.value().GetDebugString(true);
273   } else {
274     s += "(none)";
275   }
276 
277   s += "; network_anonymization_key: ";
278   s += network_anonymization_key_.ToDebugString();
279 
280   s += "; network_isolation_key: ";
281   s += network_isolation_key_.ToDebugString();
282 
283   s += "; nonce: ";
284   if (nonce_) {
285     s += nonce_.value().ToString();
286   } else {
287     s += "(none)";
288   }
289 
290   s += "; site_for_cookies: ";
291   s += site_for_cookies_.ToDebugString();
292 
293   return s;
294 }
295 
296 NetworkAnonymizationKey
CreateNetworkAnonymizationKeyForIsolationInfo(const std::optional<url::Origin> & top_frame_origin,const std::optional<url::Origin> & frame_origin,const std::optional<base::UnguessableToken> & nonce) const297 IsolationInfo::CreateNetworkAnonymizationKeyForIsolationInfo(
298     const std::optional<url::Origin>& top_frame_origin,
299     const std::optional<url::Origin>& frame_origin,
300     const std::optional<base::UnguessableToken>& nonce) const {
301   if (!top_frame_origin) {
302     return NetworkAnonymizationKey();
303   }
304   SchemefulSite top_frame_site(*top_frame_origin);
305   SchemefulSite frame_site(*frame_origin);
306 
307   return NetworkAnonymizationKey::CreateFromFrameSite(top_frame_site,
308                                                       frame_site, nonce);
309 }
310 
IsolationInfo(RequestType request_type,const std::optional<url::Origin> & top_frame_origin,const std::optional<url::Origin> & frame_origin,const SiteForCookies & site_for_cookies,const std::optional<base::UnguessableToken> & nonce)311 IsolationInfo::IsolationInfo(RequestType request_type,
312                              const std::optional<url::Origin>& top_frame_origin,
313                              const std::optional<url::Origin>& frame_origin,
314                              const SiteForCookies& site_for_cookies,
315                              const std::optional<base::UnguessableToken>& nonce)
316     : request_type_(request_type),
317       top_frame_origin_(top_frame_origin),
318       frame_origin_(frame_origin),
319       network_isolation_key_(
320           !top_frame_origin
321               ? NetworkIsolationKey()
322               : NetworkIsolationKey(SchemefulSite(*top_frame_origin),
323                                     SchemefulSite(*frame_origin),
324                                     nonce)),
325       network_anonymization_key_(
326           CreateNetworkAnonymizationKeyForIsolationInfo(top_frame_origin,
327                                                         frame_origin,
328                                                         nonce)),
329       site_for_cookies_(site_for_cookies),
330       nonce_(nonce) {
331   DCHECK(IsConsistent(request_type_, top_frame_origin_, frame_origin_,
332                       site_for_cookies_, nonce));
333 }
334 
335 }  // namespace net
336