Revision dcfc78b0
Added by jahoti about 2 years ago
| background/nonce_store.js | ||
|---|---|---|
| 1 |
/** |
|
| 2 |
* Central management of HTTP(S) nonces |
|
| 3 |
* |
|
| 4 |
* Copyright (C) 2021 jahoti |
|
| 5 |
* Redistribution terms are gathered in the `copyright' file. |
|
| 6 |
*/ |
|
| 7 |
|
|
| 8 |
/* |
|
| 9 |
* IMPORTS_START |
|
| 10 |
* IMPORT gen_nonce |
|
| 11 |
* IMPORTS_END |
|
| 12 |
*/ |
|
| 13 |
|
|
| 14 |
var nonces = {};
|
|
| 15 |
|
|
| 16 |
function retrieve_nonce(tabId, frameId, update) |
|
| 17 |
{
|
|
| 18 |
let code = tabId + '.' + frameId; |
|
| 19 |
console.log('Nonce for ' + code + ' ' + (update ? 'created/updated' : 'requested'));
|
|
| 20 |
if (update) |
|
| 21 |
nonces[code] = gen_nonce(); |
|
| 22 |
|
|
| 23 |
return nonces[code]; |
|
| 24 |
} |
|
| 25 |
|
|
| 26 |
/* |
|
| 27 |
* EXPORTS_START |
|
| 28 |
* EXPORT retrieve_nonce |
|
| 29 |
* EXPORTS_END |
|
| 30 |
*/ |
|
| background/page_actions_server.js | ||
|---|---|---|
| 11 | 11 |
* IMPORT TYPE_PREFIX |
| 12 | 12 |
* IMPORT CONNECTION_TYPE |
| 13 | 13 |
* IMPORT browser |
| 14 |
* IMPORT retrieve_nonce |
|
| 14 | 15 |
* IMPORT listen_for_connection |
| 15 | 16 |
* IMPORT sha256 |
| 16 | 17 |
* IMPORT get_query_best |
| ... | ... | |
| 137 | 138 |
function new_connection(port) |
| 138 | 139 |
{
|
| 139 | 140 |
console.log("new page actions connection!");
|
| 141 |
port.postMessage(['nonce', retrieve_nonce((port.sender.tab || '').id, port.sender.frameId)]); |
|
| 140 | 142 |
let handler = []; |
| 141 | 143 |
handler.push(m => handle_message(port, m, handler)); |
| 142 | 144 |
port.onMessage.addListener(handler[0]); |
| background/policy_injector.js | ||
|---|---|---|
| 11 | 11 |
* IMPORT get_storage |
| 12 | 12 |
* IMPORT browser |
| 13 | 13 |
* IMPORT is_chrome |
| 14 |
* IMPORT gen_unique
|
|
| 14 |
* IMPORT retrieve_nonce
|
|
| 15 | 15 |
* IMPORT url_item |
| 16 | 16 |
* IMPORT get_query_best |
| 17 | 17 |
* IMPORT csp_rule |
| ... | ... | |
| 45 | 45 |
|
| 46 | 46 |
const [pattern, settings] = query_best(url); |
| 47 | 47 |
|
| 48 |
const nonce = gen_unique(url);
|
|
| 48 |
const nonce = retrieve_nonce(details.tabId, details.frameId, true);
|
|
| 49 | 49 |
const rule = csp_rule(nonce); |
| 50 | 50 |
|
| 51 | 51 |
var headers; |
| common/misc.js | ||
|---|---|---|
| 2 | 2 |
* Myext miscellaneous operations refactored to a separate file |
| 3 | 3 |
* |
| 4 | 4 |
* Copyright (C) 2021 Wojtek Kosior |
| 5 |
* Copyright (C) 2021 jahoti |
|
| 5 | 6 |
* Redistribution terms are gathered in the `copyright' file. |
| 6 | 7 |
*/ |
| 7 | 8 |
|
| ... | ... | |
| 18 | 19 |
* generating unique, per-site value that can be computed synchronously |
| 19 | 20 |
* and is impossible to guess for a malicious website |
| 20 | 21 |
*/ |
| 22 |
|
|
| 23 |
/* Uint8toHex is a separate function not exported as (a) it's useful and (b) it will be used in crypto.subtle-based digests */ |
|
| 24 |
function Uint8toHex(data) |
|
| 25 |
{
|
|
| 26 |
let returnValue = ''; |
|
| 27 |
for (let byte of data) |
|
| 28 |
returnValue += ('00' + byte.toString(16)).slice(-2);
|
|
| 29 |
return returnValue; |
|
| 30 |
} |
|
| 31 |
|
|
| 32 |
function gen_nonce(length) // Default 16 |
|
| 33 |
{
|
|
| 34 |
let randomData = new Uint8Array(length || 16); |
|
| 35 |
crypto.getRandomValues(randomData); |
|
| 36 |
return Uint8toHex(randomData); |
|
| 37 |
} |
|
| 38 |
|
|
| 21 | 39 |
function gen_unique(url) |
| 22 | 40 |
{
|
| 23 | 41 |
return sha256(get_secure_salt() + url); |
| ... | ... | |
| 98 | 116 |
/* |
| 99 | 117 |
* EXPORTS_START |
| 100 | 118 |
* EXPORT gen_unique |
| 119 |
* EXPORT gen_nonce |
|
| 101 | 120 |
* EXPORT url_item |
| 102 | 121 |
* EXPORT url_extract_target |
| 103 | 122 |
* EXPORT csp_rule |
| content/main.js | ||
|---|---|---|
| 2 | 2 |
* Myext main content script run in all frames |
| 3 | 3 |
* |
| 4 | 4 |
* Copyright (C) 2021 Wojtek Kosior |
| 5 |
* Copyright (C) 2021 jahoti |
|
| 5 | 6 |
* Redistribution terms are gathered in the `copyright' file. |
| 6 | 7 |
*/ |
| 7 | 8 |
|
| 8 | 9 |
/* |
| 9 | 10 |
* IMPORTS_START |
| 11 |
* IMPORT CONNECTION_TYPE |
|
| 10 | 12 |
* IMPORT handle_page_actions |
| 11 | 13 |
* IMPORT url_item |
| 12 | 14 |
* IMPORT url_extract_target |
| 13 | 15 |
* IMPORT gen_unique |
| 16 |
* IMPORT gen_nonce |
|
| 14 | 17 |
* IMPORT csp_rule |
| 15 | 18 |
* IMPORT is_privileged_url |
| 16 | 19 |
* IMPORT sanitize_attributes |
| ... | ... | |
| 113 | 116 |
|
| 114 | 117 |
let meta = document.createElement("meta");
|
| 115 | 118 |
meta.setAttribute("http-equiv", "Content-Security-Policy");
|
| 116 |
meta.setAttribute("content", csp_rule(unique));
|
|
| 119 |
meta.setAttribute("content", csp_rule(nonce));
|
|
| 117 | 120 |
|
| 118 | 121 |
if (head.firstElementChild === null) |
| 119 | 122 |
head.appendChild(meta); |
| ... | ... | |
| 123 | 126 |
|
| 124 | 127 |
if (!is_privileged_url(document.URL)) {
|
| 125 | 128 |
start_activity_info_server(); |
| 126 |
handle_page_actions(unique);
|
|
| 129 |
var nonce, port = browser.runtime.connect({name : CONNECTION_TYPE.PAGE_ACTIONS});
|
|
| 127 | 130 |
|
| 128 | 131 |
if (is_http()) {
|
| 129 |
/* rely on CSP injected through webRequest */ |
|
| 132 |
/* rely on CSP injected through webRequest, at the cost of having to fetch a nonce via messaging */ |
|
| 133 |
const nonce_capturer = msg => {
|
|
| 134 |
port.onMessage.removeListener(nonce_capturer); |
|
| 135 |
handle_page_actions(msg[1], port); |
|
| 136 |
}; |
|
| 137 |
|
|
| 138 |
port.onMessage.addListener(nonce_capturer); |
|
| 139 |
|
|
| 130 | 140 |
} else if (is_whitelisted()) {
|
| 131 |
/* do not block scripts at all */ |
|
| 141 |
/* do not block scripts at all; as a result, there is no need for a green-lighted nonce */ |
|
| 142 |
handle_page_actions(null, port); |
|
| 132 | 143 |
} else {
|
| 144 |
nonce = gen_nonce(); |
|
| 145 |
handle_page_actions(nonce, port); |
|
| 133 | 146 |
block_nodes_recursively(document.documentElement); |
| 134 | 147 |
|
| 135 | 148 |
if (is_chrome) {
|
| content/page_actions.js | ||
|---|---|---|
| 7 | 7 |
|
| 8 | 8 |
/* |
| 9 | 9 |
* IMPORTS_START |
| 10 |
* IMPORT CONNECTION_TYPE |
|
| 11 | 10 |
* IMPORT browser |
| 12 | 11 |
* IMPORT report_script |
| 13 | 12 |
* IMPORT report_settings |
| ... | ... | |
| 55 | 54 |
report_script(script_text); |
| 56 | 55 |
} |
| 57 | 56 |
|
| 58 |
function handle_page_actions(script_nonce) {
|
|
| 57 |
function handle_page_actions(script_nonce, port) { // Add port as an argument so we can "pre-receive" a nonce in main.js
|
|
| 59 | 58 |
document.addEventListener("DOMContentLoaded", document_loaded);
|
| 60 |
port = browser.runtime.connect({name : CONNECTION_TYPE.PAGE_ACTIONS});
|
|
| 61 | 59 |
port.onMessage.addListener(handle_message); |
| 62 | 60 |
port.postMessage({url: document.URL});
|
| 63 | 61 |
|
| copyright | ||
|---|---|---|
| 10 | 10 |
Copyright: 2021 Wojtek Kosior <koszko@koszko.org> |
| 11 | 11 |
License: CC0 |
| 12 | 12 |
|
| 13 |
Files: manifest.json |
|
| 13 |
Files: manifest.json common/misc.js content/main.js
|
|
| 14 | 14 |
Copyright: 2021 Wojtek Kosior <koszko@koszko.org> |
| 15 | 15 |
2021 jahoti <jahoti@tilde.team> |
| 16 | 16 |
License: GPL-3+-javascript or Alicense-1.0 |
| ... | ... | |
| 46 | 46 |
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
| 47 | 47 |
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 48 | 48 |
|
| 49 |
Files: background/nonce_store.js |
|
| 50 |
Copyright: 2021 jahoti <jahoti@tilde.team> |
|
| 51 |
License: GPL-3+-javascript or Alicense-1.0 |
|
| 52 |
|
|
| 49 | 53 |
Files: content/freezer.js |
| 50 | 54 |
Copyright: 2005-2021 Giorgio Maone - https://maone.net |
| 51 | 55 |
2021 jahoti <jahoti@tilde.team> |
Also available in: Unified diff
Stop using the nonce consistently for a URL
Nonces are now randomly generated, either in the page (for non-HTTP(S) pages)
or by a background module which stores them by tab and frame IDs. In order to
support the increased variance in nonce-generating methods and allow them to
be loaded from the background, handle_page_actions is now invoked separately
according to (non-)blocking mechanism.