Commit 3bc5ad98 authored by Lukasz Anforowicz's avatar Lukasz Anforowicz Committed by Commit Bot

Set |request_initiator| to the website (fixing XHR as well).

This is a follow-up to r694827 which intended to ensure that
|request_initiator| is always set to the website (and not, for example,
to the content script's origin), but missed fixing this for XHR API.

Note that some follow-ups for r694827 (e.g. r700486 and r704100) have
already landed in M79 - these follow-ups assume that |request_initiator|
is set to the website origin everywhere.  This CL fixes this assumption
for XHR.  We should consider merging this CL to M79 (otherwise CORB and
CORS might incorrectly start to block same-origin XHRs).

This CL is also a prerequisite for starting to enforce
request_initiator_site_lock in a follow-up CL at
https://crrev.com/c/1875273

Bug: 1017448
Change-Id: I8512daf9364aaa10d76c81ef9bcc18f072a2337d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1876981
Auto-Submit: Łukasz Anforowicz <lukasza@chromium.org>
Reviewed-by: default avatarKaran Bhatia <karandeepb@chromium.org>
Reviewed-by: default avatarYutaka Hirano <yhirano@chromium.org>
Commit-Queue: Łukasz Anforowicz <lukasza@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710554}
parent 324375a9
......@@ -1028,7 +1028,12 @@ IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, ExtensionRequests) {
// The extension frame does run in the extension's process. Any requests made
// by it should not be visible to other extensions, since they won't have
// access to the request initiator.
EXPECT_EQ("Did not intercept any requests.", listener_result.message());
//
// OTOH, the content script executes fetches/XHRs as-if they were initiated by
// the webpage that the content script got injected into. Here, the webpage
// has origin of http://127.0.0.1:<some port>, and so the webRequest API
// extension should have access to the request.
EXPECT_EQ("Intercepted requests: ?contentscript", listener_result.message());
}
IN_PROC_BROWSER_TEST_F(ExtensionWebRequestApiTest, HostedAppRequest) {
......
......@@ -240,13 +240,13 @@ class CrossOriginReadBlockingExtensionTest : public ExtensionBrowserTest {
const Extension* extension() { return extension_; }
std::string CreateFetchScript(const GURL& resource) {
const char kXhrScriptTemplate[] = R"(
const char kFetchScriptTemplate[] = R"(
fetch($1)
.then(response => response.text())
.then(text => domAutomationController.send(text))
.catch(err => domAutomationController.send('error: ' + err));
)";
return content::JsReplace(kXhrScriptTemplate, resource);
return content::JsReplace(kFetchScriptTemplate, resource);
}
// Asks the test |extension_| to inject |content_script| into |web_contents|.
......@@ -328,7 +328,7 @@ class CrossOriginReadBlockingExtensionTest : public ExtensionBrowserTest {
std::string FetchHelper(const GURL& url, FetchCallback fetch_callback) {
content::DOMMessageQueue message_queue;
// Inject a content script that performs a cross-origin XHR to
// Inject a content script that performs a cross-origin fetch to
// cross-site.com.
EXPECT_TRUE(std::move(fetch_callback).Run(CreateFetchScript(url)));
......@@ -502,7 +502,7 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
// Test that verifies the current, baked-in (but not necessarily desirable
// behavior) where an extension that has permission to inject a content script
// to any page can also XHR (without CORS!) any cross-origin resource.
// to any page can also fetch (without CORS!) any cross-origin resource.
// See also https://crbug.com/846346.
IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
FromProgrammaticContentScript_NoSniffXml) {
......@@ -517,7 +517,8 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
ASSERT_EQ(url::Origin::Create(page_url),
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a cross-origin XHR to cross-site.com.
// Inject a content script that performs a cross-origin fetch to
// cross-site.com.
base::HistogramTester histograms;
GURL cross_site_resource(
embedded_test_server()->GetURL("cross-site.com", "/nosniff.xml"));
......@@ -545,7 +546,8 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
ASSERT_EQ(url::Origin::Create(page_url),
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a cross-origin XHR to cross-site.com.
// Inject a content script that performs a cross-origin fetch to
// cross-site.com.
base::HistogramTester histograms;
GURL cross_site_resource(embedded_test_server()->GetURL(
"other-without-permission.com", "/cors-ok.txt"));
......@@ -573,7 +575,7 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
ASSERT_EQ(url::Origin::Create(page_url),
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a same-origin XHR to
// Inject a content script that performs a same-origin fetch to
// fetch-initiator.com.
base::HistogramTester histograms;
GURL same_origin_resource(
......@@ -602,7 +604,8 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
ASSERT_EQ(url::Origin::Create(page_url),
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a cross-origin XHR to cross-site.com.
// Inject a content script that performs a cross-origin fetch to
// cross-site.com.
//
// StartsWith (rather than equality) is used in the verification step to
// account for \n VS \r\n difference on Windows.
......@@ -635,7 +638,8 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
ASSERT_EQ(url::Origin::Create(page_url),
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a cross-origin XHR to cross-site.com.
// Inject a content script that performs a cross-origin fetch to
// cross-site.com.
base::HistogramTester histograms;
GURL cross_site_resource(
embedded_test_server()->GetURL("cross-site.com", "/nosniff.empty"));
......@@ -656,7 +660,7 @@ IN_PROC_BROWSER_TEST_F(CrossOriginReadBlockingExtensionTest,
ASSERT_TRUE(embedded_test_server()->Start());
ASSERT_TRUE(InstallExtension());
// Performs a cross-origin XHR from the background page.
// Performs a cross-origin fetch from the background page.
base::HistogramTester histograms;
GURL cross_site_resource(
embedded_test_server()->GetURL("cross-site.com", "/nosniff.xml"));
......@@ -680,7 +684,7 @@ IN_PROC_BROWSER_TEST_F(CrossOriginReadBlockingExtensionTest,
// Test case #1: Fetch from a chrome-extension://... main frame.
{
// Perform a cross-origin XHR from the foreground extension page.
// Perform a cross-origin fetch from the foreground extension page.
base::HistogramTester histograms;
GURL cross_site_resource(
embedded_test_server()->GetURL("cross-site.com", "/nosniff.xml"));
......@@ -694,7 +698,7 @@ IN_PROC_BROWSER_TEST_F(CrossOriginReadBlockingExtensionTest,
// Test case #2: Fetch from an about:srcdoc subframe of a
// chrome-extension://... frame.
{
// Perform a cross-origin XHR from the foreground extension page.
// Perform a cross-origin fetch from the foreground extension page.
base::HistogramTester histograms;
GURL cross_site_resource(
embedded_test_server()->GetURL("cross-site.com", "/nosniff.xml"));
......@@ -765,7 +769,7 @@ IN_PROC_BROWSER_TEST_F(CrossOriginReadBlockingExtensionTest,
// created via CreateFactoryBundle called / posted indirectly from
// EmbeddedWorkerInstance::StartTask::Start.
{
// Perform a cross-origin XHR from the foreground extension page.
// Perform a cross-origin fetch from the foreground extension page.
// This should be intercepted by the service worker installed above.
base::HistogramTester histograms;
GURL cross_site_resource_intercepted_by_service_worker(
......@@ -787,7 +791,7 @@ IN_PROC_BROWSER_TEST_F(CrossOriginReadBlockingExtensionTest,
// which can be different to the network loader factory owned by the
// ServiceWorker thread (which is used in test case #1).
{
// Perform a cross-origin XHR from the foreground extension page.
// Perform a cross-origin fetch from the foreground extension page.
// This should be intercepted by the service worker installed above.
base::HistogramTester histograms;
GURL cross_site_resource_ignored_by_service_worker(
......@@ -1084,7 +1088,7 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
ASSERT_EQ(url::Origin::Create(page_url),
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a cross-origin GET XHR to
// Inject a content script that performs a cross-origin GET fetch to
// cross-site.com.
GURL cross_site_resource(
embedded_test_server()->GetURL("cross-site.com", "/echoall"));
......@@ -1137,7 +1141,7 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
ASSERT_EQ(url::Origin::Create(page_url),
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a cross-origin POST XHR to
// Inject a content script that performs a cross-origin POST fetch to
// cross-site.com.
GURL cross_site_resource(
embedded_test_server()->GetURL("cross-site.com", "/echoall"));
......@@ -1182,7 +1186,7 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
ASSERT_EQ(url::Origin::Create(page_url),
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a same-origin POST XHR to
// Inject a content script that performs a same-origin POST fetch to
// fetch-initiator.com.
GURL same_origin_resource(
embedded_test_server()->GetURL("fetch-initiator.com", "/echoall"));
......@@ -1211,7 +1215,7 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
}
IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
RequestHeaders_InSameOriginXhr_FromContentScript) {
RequestHeaders_InSameOriginFetch_FromContentScript) {
// Sec-Fetch-Site only works on secure origins - setting up a https test
// server to help with this.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
......@@ -1232,7 +1236,7 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
ASSERT_EQ(url::Origin::Create(page_url),
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a same-origin GET XHR.
// Inject a content script that performs a same-origin GET fetch.
GURL same_origin_resource(https_server.GetURL("/subresource"));
EXPECT_EQ(url::Origin::Create(page_url),
url::Origin::Create(same_origin_resource));
......@@ -1257,6 +1261,55 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
testing::Pair("Sec-Fetch-Site", expected_sec_fetch_site)}));
}
IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
RequestHeaders_InSameOriginXhr_FromContentScript) {
// Sec-Fetch-Site only works on secure origins - setting up a https test
// server to help with this.
net::EmbeddedTestServer https_server(net::EmbeddedTestServer::TYPE_HTTPS);
https_server.AddDefaultHandlers(GetChromeTestDataDir());
https_server.SetSSLConfig(net::EmbeddedTestServer::CERT_OK);
net::test_server::ControllableHttpResponse subresource_request(
&https_server, "/subresource");
ASSERT_TRUE(https_server.Start());
// Load the test extension.
ASSERT_TRUE(InstallExtension());
// Navigate to https test page.
GURL page_url = https_server.GetURL("/title1.html");
ui_test_utils::NavigateToURL(browser(), page_url);
ASSERT_EQ(page_url,
active_web_contents()->GetMainFrame()->GetLastCommittedURL());
ASSERT_EQ(url::Origin::Create(page_url),
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a same-origin GET XHR.
GURL same_origin_resource(https_server.GetURL("/subresource"));
EXPECT_EQ(url::Origin::Create(page_url),
url::Origin::Create(same_origin_resource));
const char* kScriptTemplate = R"(
var req = new XMLHttpRequest();
req.open('GET', $1, true);
req.send(null); )";
ExecuteContentScript(
active_web_contents(),
content::JsReplace(kScriptTemplate, same_origin_resource));
// Verify the Referrer and Sec-Fetch-* header values.
subresource_request.WaitForRequest();
const char* expected_sec_fetch_site = "same-origin";
if (IsExtensionAllowlisted()) {
// TODO(lukasza): https://crbug.com/998247: Even allowlisted extensions
// should correctly indicate `Sec-Fetch-Site: same-origin`.
expected_sec_fetch_site = "cross-site";
}
EXPECT_THAT(subresource_request.http_request()->headers,
testing::IsSupersetOf(
{testing::Pair("Referer", page_url.spec().c_str()),
testing::Pair("Sec-Fetch-Mode", "cors"),
testing::Pair("Sec-Fetch-Site", expected_sec_fetch_site)}));
}
IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
CorsFromContentScript) {
std::string cors_resource_path = "/cors-subresource-to-intercept";
......@@ -1275,7 +1328,7 @@ IN_PROC_BROWSER_TEST_P(CrossOriginReadBlockingExtensionAllowlistingTest,
ASSERT_EQ(page_origin,
active_web_contents()->GetMainFrame()->GetLastCommittedOrigin());
// Inject a content script that performs a cross-origin GET XHR.
// Inject a content script that performs a cross-origin GET fetch.
content::DOMMessageQueue message_queue;
GURL cors_resource_url(
embedded_test_server()->GetURL("cross-site.com", cors_resource_path));
......
......@@ -10,7 +10,7 @@ runTests([
function startXMLHttpRequestAndRemoveFrame() {
const hostname = 'slow-resourcetype-xhr-immediately-remove-frame';
const url = getSlowURL(hostname);
const initiator = 'chrome-extension://' + chrome.runtime.id;
const initiator = getServerDomain(initiators.WEB_INITIATED, hostname)
const mainUrl = getPageWithFrame('empty.html', hostname);
expect([
......@@ -81,7 +81,7 @@ runTests([
function startXMLHttpRequestAndRemoveTab() {
const hostname = 'slow-resourcetype-xhr-immediately-remove-tab';
const url = getSlowURL(hostname);
const initiator = 'chrome-extension://' + chrome.runtime.id;
const initiator = getServerDomain(initiators.WEB_INITIATED, hostname)
const mainUrl = getServerURL('empty.html', hostname);
expect([
......
......@@ -305,18 +305,6 @@ Document* XMLHttpRequest::GetDocument() const {
return To<Document>(GetExecutionContext());
}
const SecurityOrigin* XMLHttpRequest::GetSecurityOrigin() const {
return isolated_world_security_origin_
? isolated_world_security_origin_.get()
: GetExecutionContext()->GetSecurityOrigin();
}
SecurityOrigin* XMLHttpRequest::GetMutableSecurityOrigin() {
return isolated_world_security_origin_
? isolated_world_security_origin_.get()
: GetExecutionContext()->GetMutableSecurityOrigin();
}
XMLHttpRequest::State XMLHttpRequest::readyState() const {
return state_;
}
......@@ -1048,12 +1036,15 @@ void XMLHttpRequest::CreateRequest(scoped_refptr<EncodedFormData> http_body,
// We also remember whether upload events should be allowed for this request
// in case the upload listeners are added after the request is started.
upload_events_allowed_ =
GetSecurityOrigin()->CanRequest(url_) || upload_events ||
!cors::IsCorsSafelistedMethod(method_) ||
GetExecutionContext()->GetSecurityOrigin()->CanRequest(url_) ||
(isolated_world_security_origin_ &&
isolated_world_security_origin_->CanRequest(url_)) ||
upload_events || !cors::IsCorsSafelistedMethod(method_) ||
!cors::ContainsOnlyCorsSafelistedHeaders(request_headers_);
ResourceRequest request(url_);
request.SetRequestorOrigin(GetSecurityOrigin());
request.SetRequestorOrigin(GetExecutionContext()->GetSecurityOrigin());
request.SetIsolatedWorldOrigin(isolated_world_security_origin_);
request.SetHttpMethod(method_);
request.SetRequestContext(mojom::RequestContextType::XML_HTTP_REQUEST);
request.SetMode(upload_events
......@@ -1494,8 +1485,9 @@ String XMLHttpRequest::getAllResponseHeaders() const {
// TODO: Consider removing canLoadLocalResources() call.
// crbug.com/567527
if (FetchUtils::IsForbiddenResponseHeaderName(it->key) &&
!GetSecurityOrigin()->CanLoadLocalResources())
!GetExecutionContext()->GetSecurityOrigin()->CanLoadLocalResources()) {
continue;
}
if (response_.GetType() == network::mojom::FetchResponseType::kCors &&
!cors::IsCorsSafelistedResponseHeader(it->key) &&
......@@ -1530,7 +1522,7 @@ const AtomicString& XMLHttpRequest::getResponseHeader(
// See comment in getAllResponseHeaders above.
if (FetchUtils::IsForbiddenResponseHeaderName(name) &&
!GetSecurityOrigin()->CanLoadLocalResources()) {
!GetExecutionContext()->GetSecurityOrigin()->CanLoadLocalResources()) {
LogConsoleError(GetExecutionContext(),
"Refused to get unsafe header \"" + name + "\"");
return g_null_atom;
......
......@@ -176,12 +176,6 @@ class XMLHttpRequest final : public XMLHttpRequestEventTarget,
Document* GetDocument() const;
// Returns the SecurityOrigin of the isolated world if the XMLHttpRequest was
// created in an isolated world. Otherwise, returns the SecurityOrigin of the
// execution context.
const SecurityOrigin* GetSecurityOrigin() const;
SecurityOrigin* GetMutableSecurityOrigin();
void DidSendData(uint64_t bytes_sent,
uint64_t total_bytes_to_be_sent) override;
void DidReceiveResponse(uint64_t identifier,
......
<?php
if ($_SERVER["HTTP_ORIGIN"] == "http://example.com") {
header("Access-Control-Allow-Origin: http://example.com");
if ($_SERVER["HTTP_ORIGIN"] == "http://127.0.0.1:8000") {
header("Access-Control-Allow-Origin: http://127.0.0.1:8000");
if ($_SERVER["REQUEST_METHOD"] == "OPTIONS") {
header("Access-Control-Allow-Headers: X-Custom-Header");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment