From dbb72974cf8b8736fde50ba10f39841512d3e8fe Mon Sep 17 00:00:00 2001 From: Pranjal Date: Wed, 28 Mar 2018 15:45:02 -0700 Subject: [PATCH] Fixes #12223: Mitigate HSTS Fingerprinting --- app/filtering.js | 26 +++++- app/sessionStore.js | 8 +- package-lock.json | 145 ++++++++++++++++++++---------- test/unit/app/sessionStoreTest.js | 27 ++++++ 4 files changed, 155 insertions(+), 51 deletions(-) diff --git a/app/filtering.js b/app/filtering.js index 5034fc101a4..4496e4aaedd 100644 --- a/app/filtering.js +++ b/app/filtering.js @@ -364,6 +364,18 @@ function registerForHeadersReceived (session, partition) { muonCb({ cancel: true }) return } + + let parsedTargetUrl = urlParse(details.url || '') + let parsedFirstPartyUrl = urlParse(firstPartyUrl) + const trackableSecurityHeaders = ['Strict-Transport-Security', 'Expect-CT', + 'Public-Key-Pins', 'Public-Key-Pins-Report-Only'] + if (isThirdPartyHost(parsedFirstPartyUrl.hostname, parsedTargetUrl.hostname)) { + trackableSecurityHeaders.forEach(function (header) { + delete details.responseHeaders[header] + delete details.responseHeaders[header.toLowerCase()] + }) + } + for (let i = 0; i < headersReceivedFilteringFns.length; i++) { let results = headersReceivedFilteringFns[i](details, isPrivate) if (!module.exports.isResourceEnabled(results.resourceName, firstPartyUrl, isPrivate)) { @@ -381,7 +393,10 @@ function registerForHeadersReceived (session, partition) { return } } - muonCb({}) + muonCb({ + responseHeaders: details.responseHeaders, + statusLine: details.statusLine + }) }) } @@ -837,6 +852,15 @@ module.exports.clearStorageData = () => { } } +module.exports.clearHSTSData = () => { + for (let partition in registeredSessions) { + let ses = registeredSessions[partition] + setImmediate(() => { + ses.clearHSTSData.bind(ses)(() => {}) + }) + } +} + /** * Clears all session caches. */ diff --git a/app/sessionStore.js b/app/sessionStore.js index 9f51b9624de..fde8215c471 100644 --- a/app/sessionStore.js +++ b/app/sessionStore.js @@ -823,9 +823,15 @@ module.exports.runPreMigrations = (data) => { } if (data.lastAppVersion) { + let runHSTSCleanup = false + try { runHSTSCleanup = compareVersions(data.lastAppVersion, '0.22.00') < 1 } catch (e) {} + + if (runHSTSCleanup) { + filtering.clearHSTSData() + } + // Force WidevineCdm to be upgraded when last app version <= 0.18.25 let runWidevineCleanup = false - try { runWidevineCleanup = compareVersions(data.lastAppVersion, '0.18.25') < 1 } catch (e) {} if (runWidevineCleanup) { diff --git a/package-lock.json b/package-lock.json index 62272e8c11c..c64f979650b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5229,55 +5229,6 @@ } } }, - "electron-download": { - "version": "github:brave/electron-download#409b65caff14edeef1daa36a7445ba6334658d7c", - "dev": true, - "requires": { - "debug": "2.6.9", - "home-path": "1.0.5", - "minimist": "1.2.0", - "mkdirp": "0.5.1", - "mv": "2.1.1", - "nugget": "1.6.2", - "path-exists": "1.0.0", - "rc": "1.2.5" - }, - "dependencies": { - "nugget": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/nugget/-/nugget-1.6.2.tgz", - "integrity": "sha1-iMpuA7pXBqmRc/XaCQJZPWvK4Qc=", - "dev": true, - "requires": { - "debug": "2.6.9", - "minimist": "1.2.0", - "pretty-bytes": "1.0.4", - "progress-stream": "1.2.0", - "request": "2.83.0", - "single-line-log": "0.4.1", - "throttleit": "0.0.2" - } - }, - "path-exists": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz", - "integrity": "sha1-1aiZjrce83p0w06w2eum6HjuoIE=", - "dev": true - }, - "single-line-log": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-0.4.1.tgz", - "integrity": "sha1-h6VWSfdJ14PsDc2AToFA2Yc8fO4=", - "dev": true - }, - "throttleit": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", - "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", - "dev": true - } - } - }, "electron-download-tf": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/electron-download-tf/-/electron-download-tf-4.3.4.tgz", @@ -5490,6 +5441,20 @@ "integrity": "sha1-HUixB9ghJqLz4hHC6iX4A7pVGyE=", "dev": true }, + "electron-download": { + "version": "github:brave/electron-download#409b65caff14edeef1daa36a7445ba6334658d7c", + "dev": true, + "requires": { + "debug": "2.6.9", + "home-path": "1.0.5", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "mv": "2.1.1", + "nugget": "1.6.2", + "path-exists": "1.0.0", + "rc": "1.2.5" + } + }, "electron-osx-sign": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.3.2.tgz", @@ -5533,6 +5498,27 @@ "integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=", "dev": true }, + "nugget": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/nugget/-/nugget-1.6.2.tgz", + "integrity": "sha1-iMpuA7pXBqmRc/XaCQJZPWvK4Qc=", + "dev": true, + "requires": { + "debug": "2.6.9", + "minimist": "1.2.0", + "pretty-bytes": "1.0.4", + "progress-stream": "1.2.0", + "request": "2.83.0", + "single-line-log": "0.4.1", + "throttleit": "0.0.2" + } + }, + "path-exists": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz", + "integrity": "sha1-1aiZjrce83p0w06w2eum6HjuoIE=", + "dev": true + }, "plist": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/plist/-/plist-1.2.0.tgz", @@ -5545,6 +5531,18 @@ "xmldom": "0.1.27" } }, + "single-line-log": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-0.4.1.tgz", + "integrity": "sha1-h6VWSfdJ14PsDc2AToFA2Yc8fO4=", + "dev": true + }, + "throttleit": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", + "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", + "dev": true + }, "xmlbuilder": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-4.0.0.tgz", @@ -5562,6 +5560,55 @@ "requires": { "electron-download": "github:brave/electron-download#409b65caff14edeef1daa36a7445ba6334658d7c", "extract-zip": "1.6.6" + }, + "dependencies": { + "electron-download": { + "version": "github:brave/electron-download#409b65caff14edeef1daa36a7445ba6334658d7c", + "dev": true, + "requires": { + "debug": "2.6.9", + "home-path": "1.0.5", + "minimist": "1.2.0", + "mkdirp": "0.5.1", + "mv": "2.1.1", + "nugget": "1.6.2", + "path-exists": "1.0.0", + "rc": "1.2.5" + } + }, + "nugget": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/nugget/-/nugget-1.6.2.tgz", + "integrity": "sha1-iMpuA7pXBqmRc/XaCQJZPWvK4Qc=", + "dev": true, + "requires": { + "debug": "2.6.9", + "minimist": "1.2.0", + "pretty-bytes": "1.0.4", + "progress-stream": "1.2.0", + "request": "2.83.0", + "single-line-log": "0.4.1", + "throttleit": "0.0.2" + } + }, + "path-exists": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-1.0.0.tgz", + "integrity": "sha1-1aiZjrce83p0w06w2eum6HjuoIE=", + "dev": true + }, + "single-line-log": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/single-line-log/-/single-line-log-0.4.1.tgz", + "integrity": "sha1-h6VWSfdJ14PsDc2AToFA2Yc8fO4=", + "dev": true + }, + "throttleit": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-0.0.2.tgz", + "integrity": "sha1-z+34jmDADdlpe2H90qg0OptoDq8=", + "dev": true + } } }, "electron-publish": { diff --git a/test/unit/app/sessionStoreTest.js b/test/unit/app/sessionStoreTest.js index 9f730d4b19d..c36e330aebc 100644 --- a/test/unit/app/sessionStoreTest.js +++ b/test/unit/app/sessionStoreTest.js @@ -12,7 +12,9 @@ const compareVersions = require('compare-versions') require('../braveUnit') describe('sessionStore unit tests', function () { + let filtering let sessionStore + let shutdownClearHistory = false let shutdownClearAutocompleteData = false let shutdownClearAutofillData = false @@ -38,6 +40,7 @@ describe('sessionStore unit tests', function () { } } const fakeFiltering = { + clearHSTSData: () => {}, clearStorageData: () => {}, clearCache: () => {}, clearHistory: () => {} @@ -90,6 +93,7 @@ describe('sessionStore unit tests', function () { } }) mockery.registerMock('./filtering', fakeFiltering) + filtering = require('./filtering') sessionStore = require('../../../app/sessionStore') }) @@ -789,6 +793,7 @@ describe('sessionStore unit tests', function () { let localeInitSpy let backupSessionStub let runImportDefaultSettings + let clearHSTSDataSpy before(function () { runPreMigrationsSpy = sinon.spy(sessionStore, 'runPreMigrations') @@ -798,6 +803,7 @@ describe('sessionStore unit tests', function () { localeInitSpy = sinon.spy(fakeLocale, 'init') backupSessionStub = sinon.stub(sessionStore, 'backupSession') runImportDefaultSettings = sinon.spy(sessionStore, 'runImportDefaultSettings') + clearHSTSDataSpy = sinon.spy(filtering, 'clearHSTSData') }) after(function () { @@ -807,6 +813,27 @@ describe('sessionStore unit tests', function () { runPostMigrationsSpy.restore() localeInitSpy.restore() backupSessionStub.restore() + clearHSTSDataSpy.restore() + }) + + describe('check clearHSTSData invocations', function () { + describe('if lastAppVersion is 0.23', function () { + it('clearHSTSData is not invoked', function () { + let exampleState = sessionStore.defaultAppState() + exampleState.lastAppVersion = '0.23' + sessionStore.runPreMigrations(exampleState) + assert.equal(clearHSTSDataSpy.notCalled, true) + }) + }) + + describe('if lastAppVersion is 0.21', function () { + it('clearHSTSData is calledOnce', function () { + let exampleState = sessionStore.defaultAppState() + exampleState.lastAppVersion = '0.21' + sessionStore.runPreMigrations(exampleState) + assert.equal(clearHSTSDataSpy.calledOnce, true) + }) + }) }) describe('when reading the session file', function () {