• 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 
9 #include "base/check_op.h"
10 #include "base/unguessable_token.h"
11 #include "net/base/features.h"
12 #include "net/base/isolation_info.h"
13 #include "net/base/isolation_info.pb.h"
14 #include "net/base/network_anonymization_key.h"
15 #include "net/base/proxy_server.h"
16 #include "third_party/abseil-cpp/absl/types/optional.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 absl::optional<url::Origin> & top_frame_origin,const absl::optional<url::Origin> & frame_origin,const SiteForCookies & site_for_cookies,absl::optional<std::set<SchemefulSite>> party_context,const absl::optional<base::UnguessableToken> & nonce)47 bool IsConsistent(IsolationInfo::RequestType request_type,
48                   const absl::optional<url::Origin>& top_frame_origin,
49                   const absl::optional<url::Origin>& frame_origin,
50                   const SiteForCookies& site_for_cookies,
51                   absl::optional<std::set<SchemefulSite>> party_context,
52                   const absl::optional<base::UnguessableToken>& nonce) {
53   // Check for the default-constructed case.
54   if (!top_frame_origin) {
55     return request_type == IsolationInfo::RequestType::kOther &&
56            !frame_origin && !nonce && site_for_cookies.IsNull() &&
57            !party_context;
58   }
59 
60   // As long as there is a |top_frame_origin|, |site_for_cookies| must be
61   // consistent with the |top_frame_origin|.
62   if (!ValidateSameSite(*top_frame_origin, site_for_cookies))
63     return false;
64 
65   // Validate frame `frame_origin`
66   // IsolationInfo must have a `frame_origin` when frame origins are enabled
67   // and the IsolationInfo is not default-constructed.
68   if (!frame_origin) {
69     return false;
70   }
71   switch (request_type) {
72     case IsolationInfo::RequestType::kMainFrame:
73       // TODO(https://crbug.com/1056706): Check that |top_frame_origin| and
74       // |frame_origin| are the same, once the ViewSource code creates a
75       // consistent IsolationInfo object.
76       //
77       // TODO(https://crbug.com/1060631): Once CreatePartial() is removed,
78       // check if SiteForCookies is non-null if the scheme is HTTP or HTTPS.
79       //
80       // TODO(https://crbug.com/1151947): Once CreatePartial() is removed,
81       // check if party_context is non-null and empty.
82       break;
83     case IsolationInfo::RequestType::kSubFrame:
84       // For subframe navigations, the subframe's origin may not be consistent
85       // with the SiteForCookies, so SameSite cookies may be sent if there's a
86       // redirect to main frames site.
87       break;
88     case IsolationInfo::RequestType::kOther:
89       // SiteForCookies must consistent with the frame origin as well for
90       // subresources.
91       return ValidateSameSite(*frame_origin, site_for_cookies);
92   }
93   return true;
94 }
95 
96 }  // namespace
97 
IsolationInfo()98 IsolationInfo::IsolationInfo()
99     : IsolationInfo(RequestType::kOther,
100                     /*top_frame_origin=*/absl::nullopt,
101                     /*frame_origin=*/absl::nullopt,
102                     SiteForCookies(),
103                     /*nonce=*/absl::nullopt,
104                     /*party_context=*/absl::nullopt) {}
105 
106 IsolationInfo::IsolationInfo(const IsolationInfo&) = default;
107 IsolationInfo::IsolationInfo(IsolationInfo&&) = default;
108 IsolationInfo::~IsolationInfo() = default;
109 IsolationInfo& IsolationInfo::operator=(const IsolationInfo&) = default;
110 IsolationInfo& IsolationInfo::operator=(IsolationInfo&&) = default;
111 
CreateForInternalRequest(const url::Origin & top_frame_origin)112 IsolationInfo IsolationInfo::CreateForInternalRequest(
113     const url::Origin& top_frame_origin) {
114   return IsolationInfo(RequestType::kOther, top_frame_origin, top_frame_origin,
115                        SiteForCookies::FromOrigin(top_frame_origin),
116                        /*nonce=*/absl::nullopt,
117                        /*party_context=*/std::set<SchemefulSite>());
118 }
119 
CreateTransient()120 IsolationInfo IsolationInfo::CreateTransient() {
121   url::Origin opaque_origin;
122   return IsolationInfo(RequestType::kOther, opaque_origin, opaque_origin,
123                        SiteForCookies(), /*nonce=*/absl::nullopt,
124                        /*party_context=*/absl::nullopt);
125 }
126 
Deserialize(const std::string & serialized)127 absl::optional<IsolationInfo> IsolationInfo::Deserialize(
128     const std::string& serialized) {
129   proto::IsolationInfo proto;
130   if (!proto.ParseFromString(serialized))
131     return absl::nullopt;
132 
133   absl::optional<url::Origin> top_frame_origin;
134   if (proto.has_top_frame_origin())
135     top_frame_origin = url::Origin::Create(GURL(proto.top_frame_origin()));
136 
137   absl::optional<url::Origin> frame_origin;
138   if (proto.has_frame_origin())
139     frame_origin = url::Origin::Create(GURL(proto.frame_origin()));
140 
141   absl::optional<std::set<SchemefulSite>> party_context;
142   if (proto.has_party_context()) {
143     party_context = std::set<SchemefulSite>();
144     for (const auto& site : proto.party_context().site()) {
145       party_context->insert(SchemefulSite::Deserialize(site));
146     }
147   }
148 
149   return IsolationInfo::CreateIfConsistent(
150       static_cast<RequestType>(proto.request_type()),
151       std::move(top_frame_origin), std::move(frame_origin),
152       SiteForCookies::FromUrl(GURL(proto.site_for_cookies())),
153       std::move(party_context), /*nonce=*/absl::nullopt);
154 }
155 
Create(RequestType request_type,const url::Origin & top_frame_origin,const url::Origin & frame_origin,const SiteForCookies & site_for_cookies,absl::optional<std::set<SchemefulSite>> party_context,const absl::optional<base::UnguessableToken> & nonce)156 IsolationInfo IsolationInfo::Create(
157     RequestType request_type,
158     const url::Origin& top_frame_origin,
159     const url::Origin& frame_origin,
160     const SiteForCookies& site_for_cookies,
161     absl::optional<std::set<SchemefulSite>> party_context,
162     const absl::optional<base::UnguessableToken>& nonce) {
163   return IsolationInfo(request_type, top_frame_origin, frame_origin,
164                        site_for_cookies, nonce, std::move(party_context));
165 }
166 
DoNotUseCreatePartialFromNak(const net::NetworkAnonymizationKey & network_anonymization_key)167 IsolationInfo IsolationInfo::DoNotUseCreatePartialFromNak(
168     const net::NetworkAnonymizationKey& network_anonymization_key) {
169   if (!network_anonymization_key.IsFullyPopulated()) {
170     return IsolationInfo();
171   }
172 
173   url::Origin top_frame_origin =
174       network_anonymization_key.GetTopFrameSite()->site_as_origin_;
175 
176   absl::optional<url::Origin> frame_origin;
177   if (network_anonymization_key.IsCrossSite()) {
178     // If we know that the origin is cross site to the top level site, create an
179     // empty origin to use as the frame origin for the isolation info. This
180     // should be cross site with the top level origin.
181     frame_origin = url::Origin();
182   } else {
183     // If we don't know that it's cross site to the top level site, use the top
184     // frame site to set the frame origin.
185     frame_origin = top_frame_origin;
186   }
187 
188   const absl::optional<base::UnguessableToken>& nonce =
189       network_anonymization_key.GetNonce();
190 
191   auto isolation_info = IsolationInfo::Create(
192       IsolationInfo::RequestType::kOther, top_frame_origin,
193       frame_origin.value(), SiteForCookies(),
194       /*party_context=*/absl::nullopt, nonce);
195   // TODO(crbug/1343856): DCHECK isolation info is fully populated.
196   return isolation_info;
197 }
198 
CreateIfConsistent(RequestType request_type,const absl::optional<url::Origin> & top_frame_origin,const absl::optional<url::Origin> & frame_origin,const SiteForCookies & site_for_cookies,absl::optional<std::set<SchemefulSite>> party_context,const absl::optional<base::UnguessableToken> & nonce)199 absl::optional<IsolationInfo> IsolationInfo::CreateIfConsistent(
200     RequestType request_type,
201     const absl::optional<url::Origin>& top_frame_origin,
202     const absl::optional<url::Origin>& frame_origin,
203     const SiteForCookies& site_for_cookies,
204     absl::optional<std::set<SchemefulSite>> party_context,
205     const absl::optional<base::UnguessableToken>& nonce) {
206   if (!IsConsistent(request_type, top_frame_origin, frame_origin,
207                     site_for_cookies, party_context, nonce)) {
208     return absl::nullopt;
209   }
210   return IsolationInfo(request_type, top_frame_origin, frame_origin,
211                        site_for_cookies, nonce, std::move(party_context));
212 }
213 
CreateForRedirect(const url::Origin & new_origin) const214 IsolationInfo IsolationInfo::CreateForRedirect(
215     const url::Origin& new_origin) const {
216   if (request_type_ == RequestType::kOther)
217     return *this;
218 
219   if (request_type_ == RequestType::kSubFrame) {
220     return IsolationInfo(request_type_, top_frame_origin_, new_origin,
221                          site_for_cookies_, nonce_, party_context_);
222   }
223 
224   DCHECK_EQ(RequestType::kMainFrame, request_type_);
225   DCHECK(!party_context_ || party_context_->empty());
226   return IsolationInfo(request_type_, new_origin, new_origin,
227                        SiteForCookies::FromOrigin(new_origin), nonce_,
228                        party_context_);
229 }
230 
frame_origin() const231 const absl::optional<url::Origin>& IsolationInfo::frame_origin() const {
232   return frame_origin_;
233 }
234 
frame_origin_for_testing() const235 const absl::optional<url::Origin>& IsolationInfo::frame_origin_for_testing()
236     const {
237   return frame_origin_;
238 }
239 
IsEqualForTesting(const IsolationInfo & other) const240 bool IsolationInfo::IsEqualForTesting(const IsolationInfo& other) const {
241   return (request_type_ == other.request_type_ &&
242           top_frame_origin_ == other.top_frame_origin_ &&
243           frame_origin_ == other.frame_origin_ &&
244           network_isolation_key_ == other.network_isolation_key_ &&
245           network_anonymization_key_ == other.network_anonymization_key_ &&
246           nonce_ == other.nonce_ &&
247           site_for_cookies_.IsEquivalent(other.site_for_cookies_) &&
248           party_context_ == other.party_context_);
249 }
250 
ToDoUseTopFrameOriginAsWell(const url::Origin & incorrectly_used_frame_origin)251 IsolationInfo IsolationInfo::ToDoUseTopFrameOriginAsWell(
252     const url::Origin& incorrectly_used_frame_origin) {
253   return IsolationInfo(
254       RequestType::kOther, incorrectly_used_frame_origin,
255       incorrectly_used_frame_origin,
256       SiteForCookies::FromOrigin(incorrectly_used_frame_origin),
257       /*nonce=*/absl::nullopt, /*party_context=*/std::set<SchemefulSite>());
258 }
259 
Serialize() const260 std::string IsolationInfo::Serialize() const {
261   if (network_isolation_key().IsTransient())
262     return "";
263 
264   proto::IsolationInfo info;
265 
266   info.set_request_type(static_cast<int32_t>(request_type_));
267 
268   if (top_frame_origin_)
269     info.set_top_frame_origin(top_frame_origin_->Serialize());
270 
271   if (frame_origin_)
272     info.set_frame_origin(frame_origin_->Serialize());
273 
274   info.set_site_for_cookies(site_for_cookies_.RepresentativeUrl().spec());
275 
276   if (party_context_) {
277     auto* pc = info.mutable_party_context();
278     for (const auto& site : *party_context_) {
279       pc->add_site(site.Serialize());
280     }
281   }
282 
283   return info.SerializeAsString();
284 }
285 
DebugString() const286 std::string IsolationInfo::DebugString() const {
287   std::string s;
288   s += "request_type: ";
289   switch (request_type_) {
290     case IsolationInfo::RequestType::kMainFrame:
291       s += "kMainFrame";
292       break;
293     case IsolationInfo::RequestType::kSubFrame:
294       s += "kSubFrame";
295       break;
296     case IsolationInfo::RequestType::kOther:
297       s += "kOther";
298       break;
299   }
300 
301   s += "; top_frame_origin: ";
302   if (top_frame_origin_) {
303     s += top_frame_origin_.value().GetDebugString(true);
304   } else {
305     s += "(none)";
306   }
307 
308   s += "; frame_origin: ";
309   if (frame_origin_) {
310     s += frame_origin_.value().GetDebugString(true);
311   } else {
312     s += "(none)";
313   }
314 
315   s += "; network_anonymization_key: ";
316   s += network_anonymization_key_.ToDebugString();
317 
318   s += "; network_isolation_key: ";
319   s += network_isolation_key_.ToDebugString();
320 
321   s += "; party_context: ";
322   if (party_context_) {
323     s += "{";
324     for (auto& site : party_context_.value()) {
325       s += site.GetDebugString();
326       s += ", ";
327     }
328     s += "}";
329   } else {
330     s += "(none)";
331   }
332 
333   s += "; nonce: ";
334   if (nonce_) {
335     s += nonce_.value().ToString();
336   } else {
337     s += "(none)";
338   }
339 
340   s += "; site_for_cookies: ";
341   s += site_for_cookies_.ToDebugString();
342 
343   return s;
344 }
345 
346 NetworkAnonymizationKey
CreateNetworkAnonymizationKeyForIsolationInfo(const absl::optional<url::Origin> & top_frame_origin,const absl::optional<url::Origin> & frame_origin,const absl::optional<base::UnguessableToken> & nonce) const347 IsolationInfo::CreateNetworkAnonymizationKeyForIsolationInfo(
348     const absl::optional<url::Origin>& top_frame_origin,
349     const absl::optional<url::Origin>& frame_origin,
350     const absl::optional<base::UnguessableToken>& nonce) const {
351   if (!top_frame_origin) {
352     return NetworkAnonymizationKey();
353   }
354   SchemefulSite top_frame_site(*top_frame_origin);
355   SchemefulSite frame_site(*frame_origin);
356 
357   return NetworkAnonymizationKey::CreateFromFrameSite(top_frame_site,
358                                                       frame_site, nonce);
359 }
360 
IsolationInfo(RequestType request_type,const absl::optional<url::Origin> & top_frame_origin,const absl::optional<url::Origin> & frame_origin,const SiteForCookies & site_for_cookies,const absl::optional<base::UnguessableToken> & nonce,absl::optional<std::set<SchemefulSite>> party_context)361 IsolationInfo::IsolationInfo(
362     RequestType request_type,
363     const absl::optional<url::Origin>& top_frame_origin,
364     const absl::optional<url::Origin>& frame_origin,
365     const SiteForCookies& site_for_cookies,
366     const absl::optional<base::UnguessableToken>& nonce,
367     absl::optional<std::set<SchemefulSite>> party_context)
368     : request_type_(request_type),
369       top_frame_origin_(top_frame_origin),
370       frame_origin_(frame_origin),
371       network_isolation_key_(
372           !top_frame_origin
373               ? NetworkIsolationKey()
374               : NetworkIsolationKey(SchemefulSite(*top_frame_origin),
375                                     SchemefulSite(*frame_origin),
376                                     nonce)),
377       network_anonymization_key_(
378           CreateNetworkAnonymizationKeyForIsolationInfo(top_frame_origin,
379                                                         frame_origin,
380                                                         nonce)),
381       site_for_cookies_(site_for_cookies),
382       nonce_(nonce),
383       party_context_(party_context.has_value() &&
384                              party_context->size() > kPartyContextMaxSize
385                          ? absl::nullopt
386                          : party_context) {
387   DCHECK(IsConsistent(request_type_, top_frame_origin_, frame_origin_,
388                       site_for_cookies_, party_context_, nonce));
389 }
390 
391 }  // namespace net
392