Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added mixed content check for *.onion origins. #15436

Merged
merged 7 commits into from
Nov 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions browser/brave_content_browser_client_browsertest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
#include <vector>

#include "base/path_service.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "brave/browser/brave_content_browser_client.h"
#include "brave/components/brave_shields/common/brave_shield_constants.h"
#include "brave/components/constants/brave_paths.h"
#include "brave/components/constants/pref_names.h"
#include "brave/components/tor/onion_location_navigation_throttle.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_service.h"
Expand Down Expand Up @@ -483,6 +485,80 @@ IN_PROC_BROWSER_TEST_F(BraveContentBrowserClientTest,
<< "No changes on the real URL";
}

IN_PROC_BROWSER_TEST_F(BraveContentBrowserClientTest, MixedContentForOnion) {
// Don't block the mock .onion requests.
tor::OnionLocationNavigationThrottle::BlockOnionRequestsOutsideTorForTesting(
false);

const GURL onion_url =
embedded_test_server()->GetURL("test.onion", "/onion.html");
const GURL onion_upgradable_url =
embedded_test_server()->GetURL("test.onion", "/onion_upgradable.html");

ASSERT_EQ("http", onion_url.scheme());
content::WebContents* contents =
browser()->tab_strip_model()->GetActiveWebContents();
{
content::WebContentsConsoleObserver console_observer(contents);
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), onion_upgradable_url));
EXPECT_TRUE(console_observer.messages().empty());
}
{
content::WebContentsConsoleObserver console_observer(contents);
console_observer.SetPattern(
"Mixed Content: The page at 'http://test.onion*/onion.html' was loaded "
"over HTTPS, but requested an insecure element "
"'http://auto_upgradable_to_https.com/image.jpg'. This request was "
"automatically upgraded to HTTPS*");
ASSERT_TRUE(ui_test_utils::NavigateToURL(browser(), onion_url));
console_observer.Wait();
}
auto fetch = [](const std::string& resource) {
return "fetch('" + resource + "').then((response) => { console.log('" +
resource + "' + ' ' + response.statusText) })";
};
{
content::WebContentsConsoleObserver console_observer(contents);
console_observer.SetPattern(
"Mixed Content: The page at 'http://test.onion*/onion.html' was "
"loaded over HTTPS, but requested an insecure resource "
"'http://example.com*'. This request has been blocked; the content "
"must be served over HTTPS.");
const GURL resource_url =
embedded_test_server()->GetURL("example.com", "/logo-referrer.png");
const std::string kFetchScript = fetch(resource_url.spec());
ASSERT_FALSE(content::ExecJs(contents, kFetchScript));
console_observer.Wait();
}
{
auto https_server = std::make_unique<net::EmbeddedTestServer>(
net::test_server::EmbeddedTestServer::TYPE_HTTPS);
https_server->SetSSLConfig(net::EmbeddedTestServer::CERT_TEST_NAMES);
https_server->AddDefaultHandlers();
ASSERT_TRUE(https_server->Start());

content::WebContentsConsoleObserver console_observer(contents);
const auto resource_url =
https_server->GetURL("example.a.test", "/echoheader").spec();
console_observer.SetPattern(resource_url + " OK");
const std::string kFetchScript = fetch(resource_url);
ASSERT_TRUE(content::ExecJs(contents, kFetchScript));
console_observer.Wait();
}
{
content::WebContentsConsoleObserver console_observer(contents);
// logo-referrer.png sets "access-control-allow-origin: *"
const auto resource_url =
embedded_test_server()
->GetURL("example.onion", "/logo-referrer.png")
.spec();
console_observer.SetPattern(resource_url + " OK");
const std::string kFetchScript = fetch(resource_url);
ASSERT_TRUE(content::ExecJs(contents, kFetchScript));
console_observer.Wait();
}
}
diracdeltas marked this conversation as resolved.
Show resolved Hide resolved

#if BUILDFLAG(ENABLE_HANGOUT_SERVICES_EXTENSION)
IN_PROC_BROWSER_TEST_F(BraveContentBrowserClientTest,
HangoutsEnabledByDefault) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "content/browser/renderer_host/mixed_content_navigation_throttle.h"

#include "base/strings/string_util.h"

#include "src/content/browser/renderer_host/mixed_content_navigation_throttle.cc"

namespace content {

// static
bool MixedContentNavigationThrottle::DoesOriginSchemeRestrictMixedContent(
const url::Origin& origin) {
constexpr const char kOnion[] = ".onion";
diracdeltas marked this conversation as resolved.
Show resolved Hide resolved
if (base::EndsWith(origin.host(), kOnion) &&
(origin.scheme() == url::kHttpsScheme ||
origin.scheme() == url::kHttpScheme ||
origin.scheme() == url::kWsScheme ||
origin.scheme() == url::kWssScheme)) {
return true;
}
return ::content::DoesOriginSchemeRestrictMixedContent(origin);
}

} // namespace content
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_CHROMIUM_SRC_CONTENT_BROWSER_RENDERER_HOST_MIXED_CONTENT_NAVIGATION_THROTTLE_H_
#define BRAVE_CHROMIUM_SRC_CONTENT_BROWSER_RENDERER_HOST_MIXED_CONTENT_NAVIGATION_THROTTLE_H_

#define MaybeSendBlinkFeatureUsageReport \
NotUsed(); \
\
static bool DoesOriginSchemeRestrictMixedContent(const url::Origin& origin); \
\
void MaybeSendBlinkFeatureUsageReport

#include "src/content/browser/renderer_host/mixed_content_navigation_throttle.h"

#undef MaybeSendBlinkFeatureUsageReport

#endif // BRAVE_CHROMIUM_SRC_CONTENT_BROWSER_RENDERER_HOST_MIXED_CONTENT_NAVIGATION_THROTTLE_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "third_party/blink/renderer/core/loader/mixed_content_checker.h"

namespace blink {

namespace {

template <typename T>
bool IsOnion(const T& obj) {
constexpr const char kOnion[] = ".onion";
return obj.Host().EndsWith(kOnion) && (obj.Protocol() == url::kHttpsScheme ||
obj.Protocol() == url::kHttpScheme ||
obj.Protocol() == url::kWsScheme ||
obj.Protocol() == url::kWssScheme);
}

} // namespace
} // namespace blink

#define BRAVE_MIXED_CONTENT_CHECKER_IS_MIXED_CONTENT \
if (auto result = IsMixedContentForOnion(security_origin, url)) \
return *result;

#define UpgradeInsecureRequest UpgradeInsecureRequest_ChromiumImpl

#include "src/third_party/blink/renderer/core/loader/mixed_content_checker.cc"

#undef UpgradeInsecureRequest

#undef BRAVE_MIXED_CONTENT_CHECKER_IS_MIXED_CONTENT

namespace blink {
// static
absl::optional<bool> MixedContentChecker::IsMixedContentForOnion(
const SecurityOrigin* security_origin,
const KURL& resource_url) {
if (!IsOnion(*security_origin)) {
return absl::nullopt;
}
if (IsOnion(resource_url)) {
// onion -> onion: not blocked
return false;
}
// Treat .onions as https://
// onion -> https: not blocked
// onion -> http: blocked
return IsMixedContent(url::kHttpsScheme, resource_url);
}

void MixedContentChecker::UpgradeInsecureRequest(
ResourceRequest& resource_request,
const FetchClientSettingsObject* fetch_client_settings_object,
ExecutionContext* execution_context_for_logging,
mojom::RequestContextFrameType frame_type,
WebContentSettingsClient* settings_client) {
// Do not upgrade resource from .onion sites because we treat them as secure.
if (IsOnion(resource_request.Url()))
return;
goodov marked this conversation as resolved.
Show resolved Hide resolved
UpgradeInsecureRequest_ChromiumImpl(
resource_request, fetch_client_settings_object,
execution_context_for_logging, frame_type, settings_client);
}

} // namespace blink
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#ifndef BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_MIXED_CONTENT_CHECKER_H_
#define BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_MIXED_CONTENT_CHECKER_H_

#include "third_party/abseil-cpp/absl/types/optional.h"

// We are hiding IsMixedContent(const String& origin_protocol, const KURL&)
// because we want to enforce mixed content checks on .onion origins.
// Publicly available protocol-only overload of this method allows to skip
// .onion specific checks which we don't want.

#define UpgradeInsecureRequest \
diracdeltas marked this conversation as resolved.
Show resolved Hide resolved
NotUsed(); \
\
private: \
static bool IsMixedContent(const String& origin_protocol, const KURL&); \
static absl::optional<bool> IsMixedContentForOnion(const SecurityOrigin*, \
const KURL& url); \
\
public: \
static void UpgradeInsecureRequest( \
ResourceRequest&, \
const FetchClientSettingsObject* fetch_client_settings_object, \
ExecutionContext* execution_context_for_logging, \
mojom::RequestContextFrameType, \
WebContentSettingsClient* settings_client); \
static void UpgradeInsecureRequest_ChromiumImpl

#include "src/third_party/blink/renderer/core/loader/mixed_content_checker.h"

#undef UpgradeInsecureRequest

#endif // BRAVE_CHROMIUM_SRC_THIRD_PARTY_BLINK_RENDERER_CORE_LOADER_MIXED_CONTENT_CHECKER_H_
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* Copyright (c) 2022 The Brave Authors. All rights reserved.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at https://mozilla.org/MPL/2.0/. */

#include "third_party/blink/renderer/platform/loader/fetch/https_state.h"

#define CalculateHttpsState CalculateHttpsState_ChromiumImpl

#include "src/third_party/blink/renderer/platform/loader/fetch/https_state.cc"

#undef CalculateHttpsState

namespace blink {

HttpsState CalculateHttpsState(const SecurityOrigin* security_origin,
absl::optional<HttpsState> parent_https_state) {
if (security_origin && (security_origin->Protocol() == "http" &&
security_origin->Host().EndsWith(".onion"))) {
// MixedContentChecker::ShouldAutoupgrade upgrades resource only on the
// kModern pages. Return it for .onion as for https.
return HttpsState::kModern;
goodov marked this conversation as resolved.
Show resolved Hide resolved
}

return CalculateHttpsState_ChromiumImpl(security_origin, parent_https_state);
}

} // namespace blink
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker.cc b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
index ba71a1c594d976b225e00aedebba8e5cbc5ee40a..817352b00bf94497534f9a565d26b7d8f52ce64c 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker.cc
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker.cc
@@ -268,6 +268,7 @@ bool RequestIsSubframeSubresource(Frame* frame) {
// static
bool MixedContentChecker::IsMixedContent(const SecurityOrigin* security_origin,
const KURL& url) {
+ BRAVE_MIXED_CONTENT_CHECKER_IS_MIXED_CONTENT
return IsMixedContent(security_origin->Protocol(), url);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
diff --git a/third_party/blink/renderer/core/loader/mixed_content_checker.h b/third_party/blink/renderer/core/loader/mixed_content_checker.h
index 402ce521e418425943d7bd88d57fc3d1d3832da5..a64e2ed54a0005245e46b10db08b3551569d6ab0 100644
--- a/third_party/blink/renderer/core/loader/mixed_content_checker.h
+++ b/third_party/blink/renderer/core/loader/mixed_content_checker.h
@@ -98,7 +98,6 @@ class CORE_EXPORT MixedContentChecker final {
static bool IsWebSocketAllowed(WorkerFetchContext&, const KURL&);

static bool IsMixedContent(const SecurityOrigin*, const KURL&);
- static bool IsMixedContent(const String& origin_protocol, const KURL&);
goodov marked this conversation as resolved.
Show resolved Hide resolved
static bool IsMixedContent(const FetchClientSettingsObject&, const KURL&);
static bool IsMixedFormAction(
LocalFrame*,
9 changes: 9 additions & 0 deletions test/data/onion.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<html>
<head><title>OK</title></head>

<body>
<img src='http://auto_upgradable_to_https.com/image.jpg'>
<img src='https://example.com/favicon.jpg'>
<img src='http://example.onion/favicon.jpg'>
</body>
</html>
12 changes: 12 additions & 0 deletions test/data/onion_upgradable.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<html>
<head><title>OK</title></head>

<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
diracdeltas marked this conversation as resolved.
Show resolved Hide resolved

<body>
<img src='http://upgradable_to_https.com/image.jpg'>

<img src='https://example.com/favicon.jpg'>
<img src='http://example.onion/favicon.jpg'>
</body>
</html>