• 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(crbug.com/40122112): 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(crbug.com/40677006): 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(crbug.com/40122112): 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 
CreateTransientWithNonce(const base::UnguessableToken & nonce)119 IsolationInfo IsolationInfo::CreateTransientWithNonce(
120     const base::UnguessableToken& nonce) {
121   url::Origin opaque_origin;
122   return IsolationInfo(RequestType::kOther, opaque_origin, opaque_origin,
123                        SiteForCookies(), nonce);
124 }
125 
Deserialize(const std::string & serialized)126 std::optional<IsolationInfo> IsolationInfo::Deserialize(
127     const std::string& serialized) {
128   proto::IsolationInfo proto;
129   if (!proto.ParseFromString(serialized))
130     return std::nullopt;
131 
132   std::optional<url::Origin> top_frame_origin;
133   if (proto.has_top_frame_origin())
134     top_frame_origin = url::Origin::Create(GURL(proto.top_frame_origin()));
135 
136   std::optional<url::Origin> frame_origin;
137   if (proto.has_frame_origin())
138     frame_origin = url::Origin::Create(GURL(proto.frame_origin()));
139 
140   return IsolationInfo::CreateIfConsistent(
141       static_cast<RequestType>(proto.request_type()),
142       std::move(top_frame_origin), std::move(frame_origin),
143       SiteForCookies::FromUrl(GURL(proto.site_for_cookies())),
144       /*nonce=*/std::nullopt);
145 }
146 
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)147 IsolationInfo IsolationInfo::Create(
148     RequestType request_type,
149     const url::Origin& top_frame_origin,
150     const url::Origin& frame_origin,
151     const SiteForCookies& site_for_cookies,
152     const std::optional<base::UnguessableToken>& nonce) {
153   return IsolationInfo(request_type, top_frame_origin, frame_origin,
154                        site_for_cookies, nonce);
155 }
156 
DoNotUseCreatePartialFromNak(const net::NetworkAnonymizationKey & network_anonymization_key)157 IsolationInfo IsolationInfo::DoNotUseCreatePartialFromNak(
158     const net::NetworkAnonymizationKey& network_anonymization_key) {
159   if (!network_anonymization_key.IsFullyPopulated()) {
160     return IsolationInfo();
161   }
162 
163   url::Origin top_frame_origin =
164       network_anonymization_key.GetTopFrameSite()->site_as_origin_;
165 
166   std::optional<url::Origin> frame_origin;
167   if (network_anonymization_key.IsCrossSite()) {
168     // If we know that the origin is cross site to the top level site, create an
169     // empty origin to use as the frame origin for the isolation info. This
170     // should be cross site with the top level origin.
171     frame_origin = url::Origin();
172   } else {
173     // If we don't know that it's cross site to the top level site, use the top
174     // frame site to set the frame origin.
175     frame_origin = top_frame_origin;
176   }
177 
178   const std::optional<base::UnguessableToken>& nonce =
179       network_anonymization_key.GetNonce();
180 
181   auto isolation_info = IsolationInfo::Create(
182       IsolationInfo::RequestType::kOther, top_frame_origin,
183       frame_origin.value(), SiteForCookies(), nonce);
184   // TODO(crbug.com/40852603): DCHECK isolation info is fully populated.
185   return isolation_info;
186 }
187 
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)188 std::optional<IsolationInfo> IsolationInfo::CreateIfConsistent(
189     RequestType request_type,
190     const std::optional<url::Origin>& top_frame_origin,
191     const std::optional<url::Origin>& frame_origin,
192     const SiteForCookies& site_for_cookies,
193     const std::optional<base::UnguessableToken>& nonce) {
194   if (!IsConsistent(request_type, top_frame_origin, frame_origin,
195                     site_for_cookies, nonce)) {
196     return std::nullopt;
197   }
198   return IsolationInfo(request_type, top_frame_origin, frame_origin,
199                        site_for_cookies, nonce);
200 }
201 
CreateForRedirect(const url::Origin & new_origin) const202 IsolationInfo IsolationInfo::CreateForRedirect(
203     const url::Origin& new_origin) const {
204   if (request_type_ == RequestType::kOther)
205     return *this;
206 
207   if (request_type_ == RequestType::kSubFrame) {
208     return IsolationInfo(request_type_, top_frame_origin_, new_origin,
209                          site_for_cookies_, nonce_);
210   }
211 
212   DCHECK_EQ(RequestType::kMainFrame, request_type_);
213   return IsolationInfo(request_type_, new_origin, new_origin,
214                        SiteForCookies::FromOrigin(new_origin), nonce_);
215 }
216 
frame_origin() const217 const std::optional<url::Origin>& IsolationInfo::frame_origin() const {
218   return frame_origin_;
219 }
220 
IsEqualForTesting(const IsolationInfo & other) const221 bool IsolationInfo::IsEqualForTesting(const IsolationInfo& other) const {
222   return (request_type_ == other.request_type_ &&
223           top_frame_origin_ == other.top_frame_origin_ &&
224           frame_origin_ == other.frame_origin_ &&
225           network_isolation_key_ == other.network_isolation_key_ &&
226           network_anonymization_key_ == other.network_anonymization_key_ &&
227           nonce_ == other.nonce_ &&
228           site_for_cookies_.IsEquivalent(other.site_for_cookies_));
229 }
230 
Serialize() const231 std::string IsolationInfo::Serialize() const {
232   if (network_isolation_key().IsTransient())
233     return "";
234 
235   proto::IsolationInfo info;
236 
237   info.set_request_type(static_cast<int32_t>(request_type_));
238 
239   if (top_frame_origin_)
240     info.set_top_frame_origin(top_frame_origin_->Serialize());
241 
242   if (frame_origin_)
243     info.set_frame_origin(frame_origin_->Serialize());
244 
245   info.set_site_for_cookies(site_for_cookies_.RepresentativeUrl().spec());
246 
247   return info.SerializeAsString();
248 }
249 
DebugString() const250 std::string IsolationInfo::DebugString() const {
251   std::string s;
252   s += "request_type: ";
253   switch (request_type_) {
254     case IsolationInfo::RequestType::kMainFrame:
255       s += "kMainFrame";
256       break;
257     case IsolationInfo::RequestType::kSubFrame:
258       s += "kSubFrame";
259       break;
260     case IsolationInfo::RequestType::kOther:
261       s += "kOther";
262       break;
263   }
264 
265   s += "; top_frame_origin: ";
266   if (top_frame_origin_) {
267     s += top_frame_origin_.value().GetDebugString(true);
268   } else {
269     s += "(none)";
270   }
271 
272   s += "; frame_origin: ";
273   if (frame_origin_) {
274     s += frame_origin_.value().GetDebugString(true);
275   } else {
276     s += "(none)";
277   }
278 
279   s += "; network_anonymization_key: ";
280   s += network_anonymization_key_.ToDebugString();
281 
282   s += "; network_isolation_key: ";
283   s += network_isolation_key_.ToDebugString();
284 
285   s += "; nonce: ";
286   if (nonce_) {
287     s += nonce_.value().ToString();
288   } else {
289     s += "(none)";
290   }
291 
292   s += "; site_for_cookies: ";
293   s += site_for_cookies_.ToDebugString();
294 
295   return s;
296 }
297 
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)298 IsolationInfo::IsolationInfo(RequestType request_type,
299                              const std::optional<url::Origin>& top_frame_origin,
300                              const std::optional<url::Origin>& frame_origin,
301                              const SiteForCookies& site_for_cookies,
302                              const std::optional<base::UnguessableToken>& nonce)
303     : request_type_(request_type),
304       top_frame_origin_(top_frame_origin),
305       frame_origin_(frame_origin),
306       network_isolation_key_(
307           !top_frame_origin
308               ? NetworkIsolationKey()
309               : NetworkIsolationKey(SchemefulSite(*top_frame_origin),
310                                     SchemefulSite(*frame_origin),
311                                     nonce)),
312       network_anonymization_key_(
313           !top_frame_origin ? NetworkAnonymizationKey()
314                             : NetworkAnonymizationKey::CreateFromFrameSite(
315                                   SchemefulSite(*top_frame_origin),
316                                   SchemefulSite(*frame_origin),
317                                   nonce)),
318       site_for_cookies_(site_for_cookies),
319       nonce_(nonce) {
320   DCHECK(IsConsistent(request_type_, top_frame_origin_, frame_origin_,
321                       site_for_cookies_, nonce));
322 }
323 
324 }  // namespace net
325