Skip to content

Commit

Permalink
[Background Fetch] Add security checks copied from Fetch
Browse files Browse the repository at this point in the history
Adds some security checks to the Background Fetch API, based on
https://fetch.spec.whatwg.org/#main-fetch.

1. Blocks invalid URLs.

2. Blocks CSP violations.

3. Blocks blacklisted ports.

4. Blocks credentials embedded in the url.

5. Blocks protocols other than http:// and https://
   (WICG/background-fetch#44).

6. Blocks Mixed Content (with an additional restriction that
   insecure http: cannot be requested from http://127.0.0.1).

7. Blocks URLs with dangling markup.

8. Temporarily blocks requests that require a CORS preflight, as
   BackgroundFetchCrossOriginFilter cannot yet handle them safely.
   This restriction will be lifted eventually.

Bug: 711354,757441
Change-Id: I3d93c861ce4cbc9f460f61f3ed38a65131c2a620
Reviewed-on: https://chromium-review.googlesource.com/582007
Commit-Queue: John Mellor <[email protected]>
Reviewed-by: Peter Beverloo <[email protected]>
Cr-Commit-Position: refs/heads/master@{#499500}
  • Loading branch information
johnmellor authored and jakearchibald committed Nov 16, 2017
1 parent b1e4d31 commit 1d64b7f
Show file tree
Hide file tree
Showing 7 changed files with 211 additions and 0 deletions.
20 changes: 20 additions & 0 deletions background-fetch/content-security-policy.https.window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
// META: script=resources/utils.js
'use strict';

// Tests that requests blocked by Content Security Policy are rejected.
// https://w3c.github.io/webappsec-csp/#should-block-request

// This is not a comprehensive test of Content Security Policy - it is just
// intended to check that CSP checks are enabled.

var meta = document.createElement('meta');
meta.setAttribute('http-equiv', 'Content-Security-Policy');
meta.setAttribute('content', "connect-src 'none'");
document.head.appendChild(meta);

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(
t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'https://example.com'));
}, 'fetch blocked by CSP should reject');
32 changes: 32 additions & 0 deletions background-fetch/credentials-in-url.https.window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
// META: script=resources/utils.js
'use strict';

// "If parsedURL includes credentials, then throw a TypeError."
// https://fetch.spec.whatwg.org/#dom-request
// (Added by https://github.com/whatwg/fetch/issues/26).
// "A URL includes credentials if its username or password is not the empty
// string."
// https://url.spec.whatwg.org/#include-credentials

backgroundFetchTest((t, bgFetch) => {
return bgFetch.fetch(uniqueTag(), 'https://example.com');
}, 'fetch without credentials in URL should register ok');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(
t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'https://username:[email protected]'));
}, 'fetch with username and password in URL should reject');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(
t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'https://username:@example.com'));
}, 'fetch with username and empty password in URL should reject');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(
t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'https://:[email protected]'));
}, 'fetch with empty username and password in URL should reject');
17 changes: 17 additions & 0 deletions background-fetch/dangling-markup.https.window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
// META: script=resources/utils.js
'use strict';

// "If request's url's potentially-dangling-markup flag is set, and request's
// url's scheme is an HTTP(S) scheme, then set response to a network error."
// https://github.com/whatwg/fetch/pull/519
// https://github.com/whatwg/fetch/issues/546

// This is not a comprehensive test of dangling markup detection - it is just
// intended to check that detection is enabled.

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(
t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'https://example.com/?\n<'));
}, 'fetch to URL containing \\n and < should reject');
80 changes: 80 additions & 0 deletions background-fetch/mixed-content-and-allowed-schemes.https.window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
// META: script=resources/utils.js
'use strict';

// Tests that Mixed Content requests are blocked.
// https://w3c.github.io/webappsec-mixed-content/#should-block-fetch
// https://w3c.github.io/webappsec-mixed-content/#a-priori-authenticated-url
// https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy

// With an additional restriction that only https:// and loopback http://
// requests are allowed. Hence the wss:, file:, data:, etc schemes are blocked.
// https://github.com/WICG/background-fetch/issues/44

// This is not a comprehensive test of mixed content blocking - it is just
// intended to check that blocking is enabled.

backgroundFetchTest((t, bgFetch) => {
return bgFetch.fetch(uniqueTag(), 'https://example.com');
}, 'https: fetch should register ok');

backgroundFetchTest((t, bgFetch) => {
return bgFetch.fetch(uniqueTag(), 'http://127.0.0.1');
}, 'loopback IPv4 http: fetch should register ok');

backgroundFetchTest((t, bgFetch) => {
return bgFetch.fetch(uniqueTag(), 'http://[::1]');
}, 'loopback IPv6 http: fetch should register ok');

// http://localhost is not tested here since the correct behavior from
// https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
// depends on whether the UA conforms to the name resolution rules in
// https://tools.ietf.org/html/draft-west-let-localhost-be-localhost

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'http://example.com'));
}, 'non-loopback http: fetch should reject');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'http://192.0.2.0'));
}, 'non-loopback IPv4 http: fetch should reject');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'http://[2001:db8::1]'));
}, 'non-loopback IPv6 http: fetch should reject');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(t, new TypeError(),
bgFetch.fetch(uniqueTag(), ['https://example.com',
'http://example.com']));
}, 'https: and non-loopback http: fetch should reject');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(t, new TypeError(),
bgFetch.fetch(uniqueTag(), ['http://example.com',
'https://example.com']));
}, 'non-loopback http: and https: fetch should reject');


backgroundFetchTest((t, bgFetch) => {
return promise_rejects(t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'wss:127.0.0.1'));
}, 'wss: fetch should reject');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'file:///'));
}, 'file: fetch should reject');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'data:text/plain,foo'));
}, 'data: fetch should reject');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'foobar:bazqux'));
}, 'unknown scheme fetch should reject');
35 changes: 35 additions & 0 deletions background-fetch/port-blocking.https.window.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// META: script=/service-workers/service-worker/resources/test-helpers.sub.js
// META: script=resources/utils.js
'use strict';

// Tests that requests to bad ports are blocked.
// https://fetch.spec.whatwg.org/#port-blocking

// This is not a comprehensive test of blocked ports - it is just intended to
// check that blocking is enabled.

backgroundFetchTest((t, bgFetch) => {
return bgFetch.fetch(uniqueTag(), 'https://example.com');
}, 'fetch to default https port should register ok');

backgroundFetchTest((t, bgFetch) => {
return bgFetch.fetch(uniqueTag(), 'http://127.0.0.1');
}, 'fetch to default http port should register ok');

backgroundFetchTest((t, bgFetch) => {
return bgFetch.fetch(uniqueTag(), 'https://example.com:443');
}, 'fetch to port 443 should register ok');

backgroundFetchTest((t, bgFetch) => {
return bgFetch.fetch(uniqueTag(), 'https://example.com:80');
}, 'fetch to port 80 should register ok, even over https');

backgroundFetchTest((t, bgFetch) => {
return bgFetch.fetch(uniqueTag(), 'https://example.com:8080');
}, 'fetch to non-default non-bad port (8080) should register ok');

backgroundFetchTest((t, bgFetch) => {
return promise_rejects(
t, new TypeError(),
bgFetch.fetch(uniqueTag(), 'https://example.com:587'));
}, 'fetch to bad port (SMTP) should reject');
1 change: 1 addition & 0 deletions background-fetch/resources/sw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Deliberately left empty for now.
26 changes: 26 additions & 0 deletions background-fetch/resources/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
'use strict';

// Depends on /service-workers/service-worker/resources/test-helpers.sub.js
async function registerAndActivateServiceWorker(test) {
const script = 'resources/sw.js';
const scope = 'resources/scope' + location.pathname;
let serviceWorkerRegistration =
await service_worker_unregister_and_register(test, script, scope);
add_completion_callback(() => {
serviceWorkerRegistration.unregister();
});
await wait_for_state(test, serviceWorkerRegistration.installing, 'activated');
return serviceWorkerRegistration;
}

function backgroundFetchTest(func, description) {
promise_test(async t => {
const serviceWorkerRegistration = await registerAndActivateServiceWorker(t);
return func(t, serviceWorkerRegistration.backgroundFetch);
}, description);
}

let _nextBackgroundFetchTag = 0;
function uniqueTag() {
return 'tag' + _nextBackgroundFetchTag++;
}

0 comments on commit 1d64b7f

Please sign in to comment.