blob: 893549fb7d89fc7195df4985564e8aff5dc1edf4 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2017 The Chromium Authors
alexmos4bc26322017-07-01 00:57:142// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/isolated_origin_util.h"
6
Md Hasibul Hasana963a9342024-04-03 10:15:147#include <string>
8#include <string_view>
9
Hans Wennborgf95d5692020-04-22 17:43:3110#include "base/logging.h"
alexmos4bc26322017-07-01 00:57:1411#include "base/strings/string_util.h"
12#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
Domenic Denicola4d1145d2020-10-29 15:05:3313#include "services/network/public/cpp/is_potentially_trustworthy.h"
alexmos4bc26322017-07-01 00:57:1414#include "url/gurl.h"
15
Andrew Stone404880d2019-07-10 02:23:3116const char* kAllSubdomainsWildcard = "[*.]";
Andrew Stone6ed99b22019-06-07 06:14:3917
alexmos4bc26322017-07-01 00:57:1418namespace content {
19
Md Hasibul Hasana963a9342024-04-03 10:15:1420IsolatedOriginPattern::IsolatedOriginPattern(std::string_view pattern)
Andrew Stone6ed99b22019-06-07 06:14:3921 : isolate_all_subdomains_(false), is_valid_(false) {
22 Parse(pattern);
23}
24
25IsolatedOriginPattern::IsolatedOriginPattern(const url::Origin& origin)
26 : IsolatedOriginPattern(origin.GetURL().spec()) {}
27
28IsolatedOriginPattern::~IsolatedOriginPattern() = default;
29IsolatedOriginPattern::IsolatedOriginPattern(
30 const IsolatedOriginPattern& other) = default;
31IsolatedOriginPattern& IsolatedOriginPattern::operator=(
32 const IsolatedOriginPattern& other) = default;
33IsolatedOriginPattern::IsolatedOriginPattern(IsolatedOriginPattern&& other) =
34 default;
35IsolatedOriginPattern& IsolatedOriginPattern::operator=(
36 IsolatedOriginPattern&& other) = default;
37
Md Hasibul Hasana963a9342024-04-03 10:15:1438bool IsolatedOriginPattern::Parse(const std::string_view& unparsed_pattern) {
Peter Kastingb53b81912021-04-28 19:23:3039 pattern_ = std::string(unparsed_pattern);
Andrew Stone6ed99b22019-06-07 06:14:3940 origin_ = url::Origin();
41 isolate_all_subdomains_ = false;
42 is_valid_ = false;
43
44 size_t host_begin = unparsed_pattern.find(url::kStandardSchemeSeparator);
Md Hasibul Hasana963a9342024-04-03 10:15:1445 if (host_begin == std::string_view::npos || host_begin == 0) {
Andrew Stone6ed99b22019-06-07 06:14:3946 return false;
Md Hasibul Hasana963a9342024-04-03 10:15:1447 }
Andrew Stone6ed99b22019-06-07 06:14:3948
49 // Skip over the scheme separator.
50 host_begin += strlen(url::kStandardSchemeSeparator);
51 if (host_begin >= unparsed_pattern.size())
52 return false;
53
Md Hasibul Hasana963a9342024-04-03 10:15:1454 std::string_view scheme_part = unparsed_pattern.substr(0, host_begin);
55 std::string_view host_part = unparsed_pattern.substr(host_begin);
Andrew Stone6ed99b22019-06-07 06:14:3956
57 // Empty schemes or hosts are invalid for isolation purposes.
58 if (host_part.size() == 0)
59 return false;
60
Jan Wilken Dörrief05bb102020-08-18 19:35:5661 if (base::StartsWith(host_part, kAllSubdomainsWildcard)) {
Andrew Stone6ed99b22019-06-07 06:14:3962 isolate_all_subdomains_ = true;
63 host_part.remove_prefix(strlen(kAllSubdomainsWildcard));
64 }
65
66 GURL conformant_url(base::JoinString({scheme_part, host_part}, ""));
67 origin_ = url::Origin::Create(conformant_url);
68
69 // Ports are ignored when matching isolated origins (see also
70 // https://crbug.com/914511).
71 const std::string& scheme = origin_.scheme();
Scott Violeta02c94b142024-08-28 03:29:1872 int default_port = url::DefaultPortForScheme(scheme);
Andrew Stone6ed99b22019-06-07 06:14:3973 if (origin_.port() != default_port) {
74 LOG(ERROR) << "Ignoring port number in isolated origin: " << origin_;
75 origin_ = url::Origin::Create(GURL(
76 origin_.scheme() + url::kStandardSchemeSeparator + origin_.host()));
77 }
78
79 // Can't isolate subdomains of an IP address, must be a valid isolated origin
80 // after processing.
81 if ((conformant_url.HostIsIPAddress() && isolate_all_subdomains_) ||
82 !IsolatedOriginUtil::IsValidIsolatedOrigin(origin_)) {
83 origin_ = url::Origin();
84 isolate_all_subdomains_ = false;
85 return false;
86 }
87
Andrew Stone0a177fe22019-06-26 08:12:0488 DCHECK(!is_valid_ || !origin_.opaque());
Andrew Stone6ed99b22019-06-07 06:14:3989 is_valid_ = true;
90 return true;
91}
92
alexmos4bc26322017-07-01 00:57:1493// static
94bool IsolatedOriginUtil::DoesOriginMatchIsolatedOrigin(
95 const url::Origin& origin,
96 const url::Origin& isolated_origin) {
97 // Don't match subdomains if the isolated origin is an IP address.
98 if (isolated_origin.GetURL().HostIsIPAddress())
99 return origin == isolated_origin;
100
Lukasz Anforowicz25420932018-12-18 20:59:22101 // Compare scheme and hostname, but don't compare ports - see
102 // https://crbug.com/914511.
alexmos4bc26322017-07-01 00:57:14103 if (origin.scheme() != isolated_origin.scheme())
104 return false;
105
alexmos4bc26322017-07-01 00:57:14106 // Subdomains of an isolated origin are considered to be in the same isolated
107 // origin.
108 return origin.DomainIs(isolated_origin.host());
109}
110
111// static
112bool IsolatedOriginUtil::IsValidIsolatedOrigin(const url::Origin& origin) {
W. James MacLeand8d31f0f2023-05-26 19:15:57113 return IsValidIsolatedOriginImpl(origin,
114 /* is_legacy_isolated_origin_check=*/true);
Domenic Denicola4d1145d2020-10-29 15:05:33115}
116
117// static
118bool IsolatedOriginUtil::IsValidOriginForOptInIsolation(
119 const url::Origin& origin) {
120 // Per https://html.spec.whatwg.org/C/#initialise-the-document-object,
121 // non-secure contexts cannot be isolated via opt-in origin isolation.
W. James MacLeand8d31f0f2023-05-26 19:15:57122 return IsValidIsolatedOriginImpl(
123 origin, /* is_legacy_isolated_origin_check=*/false) &&
Domenic Denicola4d1145d2020-10-29 15:05:33124 network::IsOriginPotentiallyTrustworthy(origin);
125}
126
127// static
W. James MacLeanc07dc41b2022-07-25 18:52:16128bool IsolatedOriginUtil::IsValidOriginForOptOutIsolation(
129 const url::Origin& origin) {
130 // Per https://html.spec.whatwg.org/C/#initialise-the-document-object,
131 // non-secure contexts cannot be isolated via opt-in origin isolation,
132 // but we allow non-secure contexts to opt-out for legacy sites.
W. James MacLeand8d31f0f2023-05-26 19:15:57133 return IsValidIsolatedOriginImpl(origin,
134 /* is_legacy_isolated_origin_check=*/false);
W. James MacLeanc07dc41b2022-07-25 18:52:16135}
136
137// static
Domenic Denicola4d1145d2020-10-29 15:05:33138bool IsolatedOriginUtil::IsValidIsolatedOriginImpl(
139 const url::Origin& origin,
W. James MacLeand8d31f0f2023-05-26 19:15:57140 bool is_legacy_isolated_origin_check) {
Chris Palmerab5e5b52018-09-28 19:19:30141 if (origin.opaque())
alexmos4bc26322017-07-01 00:57:14142 return false;
143
144 // Isolated origins should have HTTP or HTTPS schemes. Hosts in other
145 // schemes may not be compatible with subdomain matching.
146 GURL origin_gurl = origin.GetURL();
147 if (!origin_gurl.SchemeIsHTTPOrHTTPS())
148 return false;
149
150 // IP addresses are allowed.
151 if (origin_gurl.HostIsIPAddress())
152 return true;
153
154 // Disallow hosts such as http://co.uk/, which don't have a valid
155 // registry-controlled domain. This prevents subdomain matching from
156 // grouping unrelated sites on a registry into the same origin.
Domenic Denicola4d1145d2020-10-29 15:05:33157 //
158 // This is not relevant for opt-in origin isolation, which doesn't need to
159 // match subdomains. (And it'd be bad to check this in that case, as it
160 // prohibits http://localhost/; see https://crbug.com/1142894.)
W. James MacLeand8d31f0f2023-05-26 19:15:57161 if (is_legacy_isolated_origin_check) {
Domenic Denicola4d1145d2020-10-29 15:05:33162 const bool has_registry_domain =
163 net::registry_controlled_domains::HostHasRegistryControlledDomain(
164 origin.host(),
165 net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
166 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
167 if (!has_registry_domain)
168 return false;
169 }
alexmos4bc26322017-07-01 00:57:14170
W. James MacLeand8d31f0f2023-05-26 19:15:57171 // Disallow hosts with a trailing dot for legacy isolated origins, but allow
172 // them for opt-in origin isolation since the spec says that they represent
173 // a distinct origin: https://url.spec.whatwg.org/#concept-domain.
174 // TODO(alexmos): Legacy isolated origins should probably support trailing
175 // dots as well, but enabling this would require carefully thinking about
alexmos4bc26322017-07-01 00:57:14176 // whether hosts without a trailing dot should match it.
W. James MacLeand8d31f0f2023-05-26 19:15:57177 if (is_legacy_isolated_origin_check && origin.host().back() == '.') {
alexmos4bc26322017-07-01 00:57:14178 return false;
W. James MacLeand8d31f0f2023-05-26 19:15:57179 }
alexmos4bc26322017-07-01 00:57:14180
181 return true;
182}
183
alexmos4bc26322017-07-01 00:57:14184} // namespace content