Commit e702ebbb authored by Takashi Toyoshima's avatar Takashi Toyoshima Committed by Commit Bot

OOR-CORS: tests and documents for webRequest response header injections

This patch adds test cases that an Extension injects response headers
to deceive CORS checks. If OOR-CORS is enabled, such injections need
'extraHeaders' option to work in expected ways.

Bug: 1000554
Change-Id: Ieef9223b1abd798bea215cfec1fb706aabd0a610
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1792207Reviewed-by: default avatarKaran Bhatia <karandeepb@chromium.org>
Commit-Queue: Takashi Toyoshima <toyoshim@chromium.org>
Cr-Commit-Position: refs/heads/master@{#695522}
parent f8648bfe
......@@ -119,12 +119,15 @@ complete nor stable.
</p>
<p>
<span class="availability">Starting from Chrome 79</span>, header modifications
affect Cross-Origin Resource Sharing (CORS) checks. If modified headers for
cross-origin requests do not meet the criteria, it will result in sending a CORS
preflight to ask the server if such headers can be accepted. If you really need
to modify headers in a way to violate the CORS protocol, you need to specify
<code>'extraHeaders'</code> in <code>opt_extraInfoSpec</code>.
<span class="availability">Starting from Chrome 79</span>, request header
modifications affect Cross-Origin Resource Sharing (CORS) checks. If modified
headers for cross-origin requests do not meet the criteria, it will result in
sending a CORS preflight to ask the server if such headers can be accepted.
If you really need to modify headers in a way to violate the CORS protocol, you
need to specify <code>'extraHeaders'</code> in <code>opt_extraInfoSpec</code>.
On the other hand, response header modifications do not work to deceive CORS
checks. If you need to deceive the CORS protocol, you also need to specify
<code>'extraHeaders'</code> for the response modifications.
</p>
<p>
......
......@@ -6,8 +6,8 @@
<script>
const hostname = 'cors.example.com';
const origin = location.protocol + '//' + hostname + ':' + location.port;
const path = '/extensions/api_test/webrequest/cors/accept';
const url = origin + path;
const path = location.search.substr('?path='.length);
const url = origin + '/extensions/api_test/webrequest/cors/' + path;
fetch(url);
</script>
......@@ -35,13 +35,13 @@ function registerOriginListeners(
onCompletedListener, {urls: [listeningUrlPattern]});
}
function registerHeaderInjectionListeners(extraInfoSpec) {
function registerRequestHeaderInjectionListeners(extraInfoSpec) {
const beforeSendHeadersListener = callbackPass(details => {
details.requestHeaders.push({name: 'x-foo', value: 'trigger-preflight'});
return { requestHeaders: details.requestHeaders };
return {requestHeaders: details.requestHeaders};
});
chrome.webRequest.onBeforeSendHeaders.addListener(
beforeSendHeadersListener, {urls: [listeningUrlPattern]}, extraInfoSpec);
beforeSendHeadersListener, {urls: [listeningUrlPattern]}, extraInfoSpec);
// If the 'x-foo' header is injected by |beforeSendHeadersListener| without
// 'extraHeaders' and with OOR-CORS being enabled, it triggers CORS
......@@ -66,6 +66,32 @@ function registerHeaderInjectionListeners(extraInfoSpec) {
event.addListener(
onCompletedOrErrorOccurredListener, {urls: [listeningUrlPattern]});
}
function registerResponseHeaderInjectionListeners(extraInfoSpec) {
const headersReceivedListener = details => {
details.responseHeaders.push(
{name: 'Access-Control-Allow-Origin', value: '*'});
return { responseHeaders: details.responseHeaders };
};
chrome.webRequest.onHeadersReceived.addListener(
headersReceivedListener, {urls: [listeningUrlPattern]}, extraInfoSpec);
// If the 'extraHeaders' is not specified and OOR-CORS is enabled, Chrome
// detects CORS failures before |headerReceivedListener| is called and injects
// fake headers to deceive the CORS checks.
const canInjectFakeCorsResponse =
extraInfoSpec.includes('extraHeaders') || getCorsMode() == 'blink';
const event = canInjectFakeCorsResponse ? chrome.webRequest.onCompleted :
chrome.webRequest.onErrorOccurred;
// Wait for the CORS request from the fetch.html to complete.
const onCompletedOrErrorOccurredListener = callbackPass(details => {
chrome.webRequest.onHeadersReceived.removeListener(headersReceivedListener);
event.removeListener(onCompletedOrErrorOccurredListener);
});
event.addListener(
onCompletedOrErrorOccurredListener, {urls: [listeningUrlPattern]});
}
runTests([
function testOriginHeader() {
......@@ -80,22 +106,38 @@ runTests([
registerOriginListeners(['origin'], [], ['requestHeaders', 'extraHeaders']);
// Wait for the navigation to complete.
navigateAndWait(
getServerURL('extensions/api_test/webrequest/cors/fetch.html'));
navigateAndWait(getServerURL(
'extensions/api_test/webrequest/cors/fetch.html?path=accept'));
},
function testCorsSensitiveHeaderInjectionWithoutExtraHeaders() {
registerHeaderInjectionListeners(['blocking', 'requestHeaders']);
registerRequestHeaderInjectionListeners(['blocking', 'requestHeaders']);
// Wait for the navigation to complete.
navigateAndWait(
getServerURL('extensions/api_test/webrequest/cors/fetch.html'));
navigateAndWait(getServerURL(
'extensions/api_test/webrequest/cors/fetch.html?path=accept'));
},
function testCorsSensitiveHeaderInjectionWithExtraHeaders() {
registerHeaderInjectionListeners(
registerRequestHeaderInjectionListeners(
['blocking', 'requestHeaders', 'extraHeaders']);
// Wait for the navigation to complete.
navigateAndWait(
getServerURL('extensions/api_test/webrequest/cors/fetch.html'));
navigateAndWait(getServerURL(
'extensions/api_test/webrequest/cors/fetch.html?path=accept'));
},
function testCorsResponseHeaderInjectionWithoutExtraHeaders() {
registerResponseHeaderInjectionListeners(
['blocking', 'responseHeaders']);
// Wait for the navigation to complete.
navigateAndWait(getServerURL(
'extensions/api_test/webrequest/cors/fetch.html?path=reject'));
},
function testCorsResponseHeaderInjectionWithExtraHeaders() {
registerResponseHeaderInjectionListeners(
['blocking', 'responseHeaders', 'extraHeaders']);
// Wait for the navigation to complete.
navigateAndWait(getServerURL(
'extensions/api_test/webrequest/cors/fetch.html?path=reject'));
},
]);
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