Fix problems with cross-origin redirects.
Three problems exist in the current code:
1) If a same-origin request causes a redirect to a different origin,
do not enforce access control checks for the redirect response
itself, because the request which resulted in the redirect was
same-origin.
2) If a same-origin request causes a redirect to a different origin,
use the original request's URL as the origin for the new request;
do not use a unique security origin.
3) Track whether the client (i.e., XMLHttpRequest) actually requested
that credentials be sent in the first place. When a same-origin
request redirects to a different origin, the original request will
send cookies whether requested or not, because it is same-origin.
The new cross-origin request should not send cookies unless they
were requested, so that the access control checks on the response
will succeed if the server granted "Access-Control-Allow-Origin=*".
BUG=226897
Review URL: https://chromiumcodereview.appspot.com/14557011
git-svn-id: svn://svn.chromium.org/blink/trunk@150130 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-expected.txt b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-expected.txt
index 062da2d..9051404 100644
--- a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-expected.txt
@@ -1,33 +1,24 @@
Tests that asynchronous XMLHttpRequests handle redirects according to the CORS standard.
-Testing resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi without credentials
Expecting success: false
PASS: 0
-Testing resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi& access-control-allow-origin=http://localhost:8000& access-control-allow-credentials=true
-Expecting success: false
-PASS: 0
-Testing resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi& access-control-allow-origin=http://localhost:8000& access-control-allow-credentials=true
-Expecting success: false
-PASS: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi
-Expecting success: false
-PASS: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi& access-control-allow-origin=http://localhost:8000
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi& access-control-allow-origin=http://localhost:8000 without credentials
Expecting success: true
FAIL: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://username:password@localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi& access-control-allow-origin=http://localhost:8000
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://username:password@localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi& access-control-allow-origin=http://localhost:8000 without credentials
Expecting success: false
PASS: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=foo://bar.cgi& access-control-allow-origin=http://localhost:8000
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=foo://bar.cgi& access-control-allow-origin=http://localhost:8000 without credentials
Expecting success: false
PASS: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=true& url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi& access-control-allow-origin=*
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=true& url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi& access-control-allow-origin=* without credentials
Expecting success: false
PASS: 0
-Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=false& url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi& access-control-allow-origin=*& access-control-allow-headers=x-webkit
+Testing http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=false& url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi& access-control-allow-origin=*& access-control-allow-headers=x-webkit without credentials
Expecting success: false
PASS: 0
-Testing resources/redirect-cors.php?url=http://127.0.0.1:8000/xmlhttprequest/resources/get.txt
+Testing resources/redirect-cors.php?url=http://127.0.0.1:8000/xmlhttprequest/resources/get.txt without credentials
Expecting success: true
PASS: PASS
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin-expected.txt b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin-expected.txt
new file mode 100644
index 0000000..a5d8af3
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin-expected.txt
@@ -0,0 +1,27 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi. Cannot use wildcard in Access-Control-Allow-Origin when credentials flag is true.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi. Credentials flag is true, but Access-Control-Allow-Credentials is not "true".
+Tests that asynchronous XMLHttpRequests handle redirects according to the CORS standard.
+
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi without credentials
+Expecting success: true
+PASS: PASS: Cross-domain access allowed.
+
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi with credentials
+Expecting success: false
+PASS: 0
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi without credentials
+Expecting success: true
+PASS: PASS: Cross-domain access allowed.
+
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi with credentials
+Expecting success: true
+PASS: PASS: Cross-domain access allowed.
+
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi without credentials
+Expecting success: true
+PASS: PASS: Cross-domain access allowed.
+
+Testing resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi with credentials
+Expecting success: false
+PASS: 0
+
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin.html b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin.html
new file mode 100644
index 0000000..ec891bd
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async-same-origin.html
@@ -0,0 +1,88 @@
+<p>Tests that asynchronous XMLHttpRequests handle redirects according to the CORS standard.</p>
+
+<pre id="console"></pre>
+<script>
+if (window.testRunner) {
+ testRunner.dumpAsText();
+ testRunner.waitUntilDone();
+}
+
+function log(message)
+{
+ document.getElementById('console').appendChild(document.createTextNode(message + '\n'));
+}
+
+function runTestAsync(url, credentials, addCustomHeader, expectSuccess) {
+ log("Testing " + url + (credentials ? " with " : " without ") + "credentials");
+ log("Expecting success: " + expectSuccess);
+
+ xhr = new XMLHttpRequest();
+ xhr.withCredentials = credentials;
+ xhr.open("GET", url, true);
+ if (addCustomHeader)
+ xhr.setRequestHeader("x-webkit", "foo");
+
+ xhr.onload = function() {
+ log((expectSuccess ? "PASS" : "FAIL") + ": " + xhr.responseText);
+ nextTest();
+ }
+ xhr.onerror = function() {
+ log((expectSuccess ? "FAIL" : "PASS") + ": " + xhr.status);
+ nextTest();
+ }
+ xhr.send(null);
+}
+
+var withoutCredentials = false;
+var withCredentials = true;
+var noCustomHeader = false;
+var addCustomHeader = true;
+var succeeds = true;
+var fails = false;
+
+var tests = [
+// Test simple same origin requests that receive cross origin redirects.
+
+// Request without credentials is redirected to a cross-origin response with Access-Control-Allow-Origin=*.
+// The redirect response passes the access check.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi",
+ withoutCredentials, noCustomHeader, succeeds],
+
+// Request with credentials is redirected to a cross-origin response with Access-Control-Allow-Origin=*.
+// The redirect response fails the access check because credentials were sent.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi",
+ withCredentials, noCustomHeader, fails],
+
+// Request without credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin.
+// The redirect response passes the access check.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi",
+ withoutCredentials, noCustomHeader, succeeds],
+
+// Request with credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin.
+// The redirect response passes the access check.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi",
+ withCredentials, noCustomHeader, succeeds],
+
+// Request without credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin
+// forbidding credentials. The redirect response passes the access check.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi",
+ withoutCredentials, noCustomHeader, succeeds],
+
+// Request with credentials is redirected to a cross-origin response with a specific Access-Control-Allow-Origin
+// forbidding credentials. The redirect response fails the access check.
+["resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi",
+ withCredentials, noCustomHeader, fails],
+
+]
+
+var currentTest = 0;
+
+function nextTest() {
+ if (currentTest < tests.length)
+ runTestAsync.apply(null, tests[currentTest++]);
+ else if (window.testRunner)
+ testRunner.notifyDone();
+}
+
+nextTest();
+</script>
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async.html b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async.html
index 086a16c..83fc3b6 100644
--- a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async.html
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-async.html
@@ -12,11 +12,12 @@
document.getElementById('console').appendChild(document.createTextNode(message + '\n'));
}
-function runTestAsync(url, addCustomHeader, expectSuccess) {
- log("Testing " + url);
+function runTestAsync(url, credentials, addCustomHeader, expectSuccess) {
+ log("Testing " + url + (credentials ? " with " : " without ") + "credentials");
log("Expecting success: " + expectSuccess);
xhr = new XMLHttpRequest();
+ xhr.withCredentials = credentials;
xhr.open("GET", url, true);
if (addCustomHeader)
xhr.setRequestHeader("x-webkit", "foo");
@@ -32,72 +33,57 @@
xhr.send(null);
}
+var withoutCredentials = false;
+var withCredentials = true;
var noCustomHeader = false;
var addCustomHeader = true;
var succeeds = true;
var fails = false;
var tests = [
-// 1) Test simple same origin requests that receive cross origin redirects.
-
-// Request receives a cross-origin redirect response without CORS headers. The redirect response fails the access check.
-["resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi",
- noCustomHeader, fails],
-
-// Request receives a cross-origin redirect response with CORS headers. The redirect response passes the access check,
-// but the resource response fails its access check because the security origin is a globally unique identifier after
-// the redirect and the same origin XHR has 'allowCredentials' true.
-["resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&\
- access-control-allow-origin=http://localhost:8000&\
- access-control-allow-credentials=true",
- noCustomHeader, fails],
-
-// Same as above, but to a less permissive resource that only allows the requesting origin.
-["resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi&\
- access-control-allow-origin=http://localhost:8000&\
- access-control-allow-credentials=true",
- noCustomHeader, fails],
-
-// 2) Test simple cross origin requests that receive redirects.
+// 1) Test simple cross origin requests that receive redirects.
// Receives a redirect response without CORS headers. The redirect response fails the access check.
["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi",
- noCustomHeader, fails],
+ withoutCredentials, noCustomHeader, fails],
// Receives a redirect response with CORS headers. The redirect response passes the access check and the resource response
// passes the access check.
+// FIXME: this test fails because the redirect is vetoed. There are continued bugs with redirects when the original
+// request was cross-origin.
["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&\
access-control-allow-origin=http://localhost:8000",
- noCustomHeader, succeeds],
+ withoutCredentials, noCustomHeader, succeeds],
// Receives a redirect response with a URL containing the userinfo production.
["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=http://username:password@localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&\
access-control-allow-origin=http://localhost:8000",
- noCustomHeader, fails],
+ withoutCredentials, noCustomHeader, fails],
// Receives a redirect response with a URL with an unsupported scheme.
["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?url=foo://bar.cgi&\
access-control-allow-origin=http://localhost:8000",
- noCustomHeader, fails],
+ withoutCredentials, noCustomHeader, fails],
-// 3) Test preflighted cross origin requests that receive redirects.
+// 2) Test preflighted cross origin requests that receive redirects.
// Receives a redirect response to the preflight request and fails.
["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=true&\
url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&\
access-control-allow-origin=*",
- addCustomHeader, fails],
+ withoutCredentials, addCustomHeader, fails],
// Successful preflight and receives a redirect response to the actual request and fails.
["http://localhost:8000/xmlhttprequest/resources/redirect-cors.php?redirect-preflight=false&\
url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow-star.cgi&\
access-control-allow-origin=*&\
access-control-allow-headers=x-webkit",
- addCustomHeader, fails],
+ withoutCredentials, addCustomHeader, fails],
-// 4) Test same origin requests with a custom header that receive a same origin redirect.
+// 3) Test same origin requests with a custom header that receive a same origin redirect.
["resources/redirect-cors.php?url=http://127.0.0.1:8000/xmlhttprequest/resources/get.txt",
- addCustomHeader, succeeds],
+ withoutCredentials, addCustomHeader, succeeds],
+
]
var currentTest = 0;
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-expected.txt b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-expected.txt
index 4471326..e5b2fe7 100644
--- a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects-expected.txt
@@ -6,8 +6,9 @@
Expecting success: false
PASS: Error: NetworkError: DOM Exception 19
Testing /resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi(async)
-Expecting success: false
-PASS: 0
+Expecting success: true
+PASS: PASS: Cross-domain access allowed.
+
Testing http://localhost:8000/resources/redirect.php?url=http://127.0.0.1:8000/xmlhttprequest/resources/access-control-basic-allow.cgi (sync)
Expecting success: false
PASS: Error: NetworkError: DOM Exception 19
diff --git a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html
index 27f55be..9792c36 100644
--- a/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html
+++ b/LayoutTests/http/tests/xmlhttprequest/access-control-and-redirects.html
@@ -45,7 +45,7 @@
}
var tests = [
- ["/resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi", false, false],
+ ["/resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi", false, true],
["http://localhost:8000/resources/redirect.php?url=http://127.0.0.1:8000/xmlhttprequest/resources/access-control-basic-allow.cgi", false, false],
["http://localhost:8000/resources/redirect.php?url=http://localhost:8000/xmlhttprequest/resources/access-control-basic-allow.cgi", false, false]
]
diff --git a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-2-expected.txt b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-2-expected.txt
index 14ecdda..a187ec2 100644
--- a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-2-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-2-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/reply.xml. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
Test that a cross-origin redirect to a server that responds is indistinguishable from one that does not. Should say PASS:
PASS
diff --git a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-expected.txt b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-expected.txt
index 14ecdda..a187ec2 100644
--- a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/reply.xml. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
Test that a cross-origin redirect to a server that responds is indistinguishable from one that does not. Should say PASS:
PASS
diff --git a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-post-expected.txt b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-post-expected.txt
index 14ecdda..a187ec2 100644
--- a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-post-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-post-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/xmlhttprequest/resources/reply.xml. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
Test that a cross-origin redirect to a server that responds is indistinguishable from one that does not. Should say PASS:
PASS
diff --git a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-tripmine-expected.txt b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-tripmine-expected.txt
index a507a97..3bfe79b 100644
--- a/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-tripmine-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/redirect-cross-origin-tripmine-expected.txt
@@ -1,3 +1,13 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8000/resources/tripmine.php. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
Test that a cross-origin redirect does not result in a non-simple request being sent to the target.
Asynchronous XMLHttpRequest 307 POST redirect:
diff --git a/LayoutTests/http/tests/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi b/LayoutTests/http/tests/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi
new file mode 100755
index 0000000..da20100
--- /dev/null
+++ b/LayoutTests/http/tests/xmlhttprequest/resources/access-control-basic-allow-no-credentials.cgi
@@ -0,0 +1,7 @@
+#!/usr/bin/perl -wT
+use strict;
+
+print "Content-Type: text/plain\n";
+print "Access-Control-Allow-Origin: http://127.0.0.1:8000\n\n";
+
+print "PASS: Cross-domain access allowed.\n";
diff --git a/LayoutTests/http/tests/xmlhttprequest/xmlhttprequest-unsafe-redirect-expected.txt b/LayoutTests/http/tests/xmlhttprequest/xmlhttprequest-unsafe-redirect-expected.txt
index df9988a..ecaf62f 100644
--- a/LayoutTests/http/tests/xmlhttprequest/xmlhttprequest-unsafe-redirect-expected.txt
+++ b/LayoutTests/http/tests/xmlhttprequest/xmlhttprequest-unsafe-redirect-expected.txt
@@ -1,3 +1,4 @@
+CONSOLE MESSAGE: XMLHttpRequest cannot load http://localhost:8080/xmlhttprequest/resources/forbidden.txt. Origin http://127.0.0.1:8000 is not allowed by Access-Control-Allow-Origin.
This tests that unsafe redirects won't be allowed when making an XMLHttpRequest.
Sync XHR started.
readyState change 1
diff --git a/Source/core/loader/DocumentLoader.cpp b/Source/core/loader/DocumentLoader.cpp
index 1aac7ce..eea87e8 100644
--- a/Source/core/loader/DocumentLoader.cpp
+++ b/Source/core/loader/DocumentLoader.cpp
@@ -1077,7 +1077,7 @@
ResourceRequest request(m_request);
DEFINE_STATIC_LOCAL(ResourceLoaderOptions, mainResourceLoadOptions,
- (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck));
+ (SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck));
CachedResourceRequest cachedResourceRequest(request, mainResourceLoadOptions);
m_mainResource = m_cachedResourceLoader->requestMainResource(cachedResourceRequest);
if (!m_mainResource) {
diff --git a/Source/core/loader/DocumentThreadableLoader.cpp b/Source/core/loader/DocumentThreadableLoader.cpp
index 5ae09a9..a40b7fc 100644
--- a/Source/core/loader/DocumentThreadableLoader.cpp
+++ b/Source/core/loader/DocumentThreadableLoader.cpp
@@ -187,7 +187,8 @@
}
// When using access control, only simple cross origin requests are allowed to redirect. The new request URL must have a supported
- // scheme and not contain the userinfo production. In addition, the redirect response must pass the access control check.
+ // scheme and not contain the userinfo production. In addition, the redirect response must pass the access control check if the
+ // original request was not same-origin.
if (m_options.crossOriginRequestPolicy == UseAccessControl) {
bool allowRedirect = false;
if (m_simpleRequest) {
@@ -195,7 +196,7 @@
allowRedirect = SchemeRegistry::shouldTreatURLSchemeAsCORSEnabled(request.url().protocol())
&& request.url().user().isEmpty()
&& request.url().pass().isEmpty()
- && passesAccessControlCheck(redirectResponse, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription);
+ && (m_sameOriginRequest || passesAccessControlCheck(redirectResponse, m_options.allowCredentials, securityOrigin(), accessControlErrorDescription));
}
if (allowRedirect) {
@@ -204,12 +205,19 @@
RefPtr<SecurityOrigin> originalOrigin = SecurityOrigin::createFromString(redirectResponse.url());
RefPtr<SecurityOrigin> requestOrigin = SecurityOrigin::createFromString(request.url());
- // If the request URL origin is not same origin with the original URL origin, set source origin to a globally unique identifier.
- if (!originalOrigin->isSameSchemeHostPort(requestOrigin.get()))
+ // If the original request wasn't same-origin, then if the request URL origin is not same origin with the original URL origin,
+ // set the source origin to a globally unique identifier. (If the original request was same-origin, the origin of the new request
+ // should be the original URL origin.)
+ if (!m_sameOriginRequest && !originalOrigin->isSameSchemeHostPort(requestOrigin.get()))
m_options.securityOrigin = SecurityOrigin::createUnique();
// Force any subsequent requests to use these checks.
m_sameOriginRequest = false;
+ // Since the request is no longer same-origin, if the user didn't request credentials in
+ // the first place, update our state so we neither request them nor expect they must be allowed.
+ if (m_options.credentialsRequested == ClientDidNotRequestCredentials)
+ m_options.allowCredentials = DoNotAllowStoredCredentials;
+
// Remove any headers that may have been added by the network layer that cause access control to fail.
request.clearHTTPContentType();
request.clearHTTPReferrer();
diff --git a/Source/core/loader/ResourceLoaderOptions.h b/Source/core/loader/ResourceLoaderOptions.h
index ad1adaa..88c1fc0 100644
--- a/Source/core/loader/ResourceLoaderOptions.h
+++ b/Source/core/loader/ResourceLoaderOptions.h
@@ -61,12 +61,28 @@
};
struct ResourceLoaderOptions {
- ResourceLoaderOptions() : sendLoadCallbacks(DoNotSendCallbacks), sniffContent(DoNotSniffContent), dataBufferingPolicy(BufferData), allowCredentials(DoNotAllowStoredCredentials), crossOriginCredentialPolicy(DoNotAskClientForCrossOriginCredentials), securityCheck(DoSecurityCheck) { }
- ResourceLoaderOptions(SendCallbackPolicy sendLoadCallbacks, ContentSniffingPolicy sniffContent, DataBufferingPolicy dataBufferingPolicy, StoredCredentials allowCredentials, ClientCrossOriginCredentialPolicy crossOriginCredentialPolicy, SecurityCheckPolicy securityCheck)
+ ResourceLoaderOptions()
+ : sendLoadCallbacks(DoNotSendCallbacks)
+ , sniffContent(DoNotSniffContent)
+ , dataBufferingPolicy(BufferData)
+ , allowCredentials(DoNotAllowStoredCredentials)
+ , credentialsRequested(ClientDidNotRequestCredentials)
+ , crossOriginCredentialPolicy(DoNotAskClientForCrossOriginCredentials)
+ , securityCheck(DoSecurityCheck) { }
+
+ ResourceLoaderOptions(
+ SendCallbackPolicy sendLoadCallbacks,
+ ContentSniffingPolicy sniffContent,
+ DataBufferingPolicy dataBufferingPolicy,
+ StoredCredentials allowCredentials,
+ CredentialRequest credentialsRequested,
+ ClientCrossOriginCredentialPolicy crossOriginCredentialPolicy,
+ SecurityCheckPolicy securityCheck)
: sendLoadCallbacks(sendLoadCallbacks)
, sniffContent(sniffContent)
, dataBufferingPolicy(dataBufferingPolicy)
, allowCredentials(allowCredentials)
+ , credentialsRequested(credentialsRequested)
, crossOriginCredentialPolicy(crossOriginCredentialPolicy)
, securityCheck(securityCheck)
{
@@ -75,6 +91,7 @@
ContentSniffingPolicy sniffContent;
DataBufferingPolicy dataBufferingPolicy;
StoredCredentials allowCredentials; // Whether HTTP credentials and cookies are sent with the request.
+ CredentialRequest credentialsRequested; // Whether the client (e.g. XHR) wanted credentials in the first place.
ClientCrossOriginCredentialPolicy crossOriginCredentialPolicy; // Whether we will ask the client for credentials (if we allow credentials at all).
SecurityCheckPolicy securityCheck;
};
diff --git a/Source/core/loader/cache/CachedResourceLoader.cpp b/Source/core/loader/cache/CachedResourceLoader.cpp
index ca469a6..d43b915 100644
--- a/Source/core/loader/cache/CachedResourceLoader.cpp
+++ b/Source/core/loader/cache/CachedResourceLoader.cpp
@@ -186,7 +186,7 @@
memoryCache()->remove(existing);
}
- request.setOptions(ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck));
+ request.setOptions(ResourceLoaderOptions(DoNotSendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, SkipSecurityCheck));
return static_cast<CachedCSSStyleSheet*>(requestResource(CachedResource::CSSStyleSheet, request).get());
}
@@ -1042,7 +1042,7 @@
const ResourceLoaderOptions& CachedResourceLoader::defaultCachedResourceOptions()
{
- static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, AskClientForCrossOriginCredentials, DoSecurityCheck);
+ static ResourceLoaderOptions options(SendCallbacks, SniffContent, BufferData, AllowStoredCredentials, ClientRequestedCredentials, AskClientForCrossOriginCredentials, DoSecurityCheck);
return options;
}
diff --git a/Source/core/page/EventSource.cpp b/Source/core/page/EventSource.cpp
index dc60773..a9deb36 100644
--- a/Source/core/page/EventSource.cpp
+++ b/Source/core/page/EventSource.cpp
@@ -131,6 +131,7 @@
options.sendLoadCallbacks = SendCallbacks;
options.sniffContent = DoNotSniffContent;
options.allowCredentials = (origin->canRequest(m_url) || m_withCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
+ options.credentialsRequested = m_withCredentials ? ClientRequestedCredentials : ClientDidNotRequestCredentials;
options.preflightPolicy = PreventPreflight;
options.crossOriginRequestPolicy = UseAccessControl;
options.dataBufferingPolicy = DoNotBufferData;
diff --git a/Source/core/platform/network/ResourceHandleTypes.h b/Source/core/platform/network/ResourceHandleTypes.h
index 870e795..4dde871 100644
--- a/Source/core/platform/network/ResourceHandleTypes.h
+++ b/Source/core/platform/network/ResourceHandleTypes.h
@@ -33,6 +33,15 @@
DoNotAllowStoredCredentials
};
+// APIs like XMLHttpRequest and EventSource let the user decide
+// whether to send credentials, but they're always sent for
+// same-origin requests. Additional information is needed to handle
+// cross-origin redirects correctly.
+enum CredentialRequest {
+ ClientRequestedCredentials,
+ ClientDidNotRequestCredentials
+};
+
} // namespace WebCore
#endif // ResourceHandleTypes_h
diff --git a/Source/core/xml/XMLHttpRequest.cpp b/Source/core/xml/XMLHttpRequest.cpp
index d173c52..578ba58 100644
--- a/Source/core/xml/XMLHttpRequest.cpp
+++ b/Source/core/xml/XMLHttpRequest.cpp
@@ -749,6 +749,7 @@
options.sniffContent = DoNotSniffContent;
options.preflightPolicy = uploadEvents ? ForcePreflight : ConsiderPreflight;
options.allowCredentials = (m_sameOriginRequest || m_includeCredentials) ? AllowStoredCredentials : DoNotAllowStoredCredentials;
+ options.credentialsRequested = m_includeCredentials ? ClientRequestedCredentials : ClientDidNotRequestCredentials;
options.crossOriginRequestPolicy = UseAccessControl;
options.securityOrigin = securityOrigin();
options.initiator = cachedResourceRequestInitiators().xmlhttprequest;