1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/isolated_origin_util.h"
#include <string>
#include <string_view>
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "url/gurl.h"
const char* kAllSubdomainsWildcard = "[*.]";
namespace content {
IsolatedOriginPattern::IsolatedOriginPattern(std::string_view pattern)
: isolate_all_subdomains_(false), is_valid_(false) {
Parse(pattern);
}
IsolatedOriginPattern::IsolatedOriginPattern(const url::Origin& origin)
: IsolatedOriginPattern(origin.GetURL().spec()) {}
IsolatedOriginPattern::~IsolatedOriginPattern() = default;
IsolatedOriginPattern::IsolatedOriginPattern(
const IsolatedOriginPattern& other) = default;
IsolatedOriginPattern& IsolatedOriginPattern::operator=(
const IsolatedOriginPattern& other) = default;
IsolatedOriginPattern::IsolatedOriginPattern(IsolatedOriginPattern&& other) =
default;
IsolatedOriginPattern& IsolatedOriginPattern::operator=(
IsolatedOriginPattern&& other) = default;
bool IsolatedOriginPattern::Parse(const std::string_view& unparsed_pattern) {
pattern_ = std::string(unparsed_pattern);
origin_ = url::Origin();
isolate_all_subdomains_ = false;
is_valid_ = false;
size_t host_begin = unparsed_pattern.find(url::kStandardSchemeSeparator);
if (host_begin == std::string_view::npos || host_begin == 0) {
return false;
}
// Skip over the scheme separator.
host_begin += strlen(url::kStandardSchemeSeparator);
if (host_begin >= unparsed_pattern.size())
return false;
std::string_view scheme_part = unparsed_pattern.substr(0, host_begin);
std::string_view host_part = unparsed_pattern.substr(host_begin);
// Empty schemes or hosts are invalid for isolation purposes.
if (host_part.size() == 0)
return false;
if (base::StartsWith(host_part, kAllSubdomainsWildcard)) {
isolate_all_subdomains_ = true;
host_part.remove_prefix(strlen(kAllSubdomainsWildcard));
}
GURL conformant_url(base::JoinString({scheme_part, host_part}, ""));
origin_ = url::Origin::Create(conformant_url);
// Ports are ignored when matching isolated origins (see also
// https://crbug.com/914511).
const std::string& scheme = origin_.scheme();
int default_port = url::DefaultPortForScheme(scheme);
if (origin_.port() != default_port) {
LOG(ERROR) << "Ignoring port number in isolated origin: " << origin_;
origin_ = url::Origin::Create(GURL(
origin_.scheme() + url::kStandardSchemeSeparator + origin_.host()));
}
// Can't isolate subdomains of an IP address, must be a valid isolated origin
// after processing.
if ((conformant_url.HostIsIPAddress() && isolate_all_subdomains_) ||
!IsolatedOriginUtil::IsValidIsolatedOrigin(origin_)) {
origin_ = url::Origin();
isolate_all_subdomains_ = false;
return false;
}
DCHECK(!is_valid_ || !origin_.opaque());
is_valid_ = true;
return true;
}
// static
bool IsolatedOriginUtil::DoesOriginMatchIsolatedOrigin(
const url::Origin& origin,
const url::Origin& isolated_origin) {
// Don't match subdomains if the isolated origin is an IP address.
if (isolated_origin.GetURL().HostIsIPAddress())
return origin == isolated_origin;
// Compare scheme and hostname, but don't compare ports - see
// https://crbug.com/914511.
if (origin.scheme() != isolated_origin.scheme())
return false;
// Subdomains of an isolated origin are considered to be in the same isolated
// origin.
return origin.DomainIs(isolated_origin.host());
}
// static
bool IsolatedOriginUtil::IsValidIsolatedOrigin(const url::Origin& origin) {
return IsValidIsolatedOriginImpl(origin,
/* is_legacy_isolated_origin_check=*/true);
}
// static
bool IsolatedOriginUtil::IsValidOriginForOptInIsolation(
const url::Origin& origin) {
// Per https://html.spec.whatwg.org/C/#initialise-the-document-object,
// non-secure contexts cannot be isolated via opt-in origin isolation.
return IsValidIsolatedOriginImpl(
origin, /* is_legacy_isolated_origin_check=*/false) &&
network::IsOriginPotentiallyTrustworthy(origin);
}
// static
bool IsolatedOriginUtil::IsValidOriginForOptOutIsolation(
const url::Origin& origin) {
// Per https://html.spec.whatwg.org/C/#initialise-the-document-object,
// non-secure contexts cannot be isolated via opt-in origin isolation,
// but we allow non-secure contexts to opt-out for legacy sites.
return IsValidIsolatedOriginImpl(origin,
/* is_legacy_isolated_origin_check=*/false);
}
// static
bool IsolatedOriginUtil::IsValidIsolatedOriginImpl(
const url::Origin& origin,
bool is_legacy_isolated_origin_check) {
if (origin.opaque())
return false;
// Isolated origins should have HTTP or HTTPS schemes. Hosts in other
// schemes may not be compatible with subdomain matching.
GURL origin_gurl = origin.GetURL();
if (!origin_gurl.SchemeIsHTTPOrHTTPS())
return false;
// IP addresses are allowed.
if (origin_gurl.HostIsIPAddress())
return true;
// Disallow hosts such as http://co.uk/, which don't have a valid
// registry-controlled domain. This prevents subdomain matching from
// grouping unrelated sites on a registry into the same origin.
//
// This is not relevant for opt-in origin isolation, which doesn't need to
// match subdomains. (And it'd be bad to check this in that case, as it
// prohibits http://localhost/; see https://crbug.com/1142894.)
if (is_legacy_isolated_origin_check) {
const bool has_registry_domain =
net::registry_controlled_domains::HostHasRegistryControlledDomain(
origin.host(),
net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES,
net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
if (!has_registry_domain)
return false;
}
// Disallow hosts with a trailing dot for legacy isolated origins, but allow
// them for opt-in origin isolation since the spec says that they represent
// a distinct origin: https://url.spec.whatwg.org/#concept-domain.
// TODO(alexmos): Legacy isolated origins should probably support trailing
// dots as well, but enabling this would require carefully thinking about
// whether hosts without a trailing dot should match it.
if (is_legacy_isolated_origin_check && origin.host().back() == '.') {
return false;
}
return true;
}
} // namespace content
|