Revision 8b823e1a
Added by jahoti about 2 years ago
background/policy_injector.js | ||
---|---|---|
12 | 12 |
* IMPORT get_storage |
13 | 13 |
* IMPORT browser |
14 | 14 |
* IMPORT is_chrome |
15 |
* IMPORT is_mozilla |
|
15 | 16 |
* IMPORT gen_unique |
16 | 17 |
* IMPORT gen_nonce |
17 | 18 |
* IMPORT url_item |
... | ... | |
45 | 46 |
function url_inject(details) |
46 | 47 |
{ |
47 | 48 |
const targets = url_extract_policy(details.url); |
48 |
if (targets.policy) {
|
|
49 |
if (targets.valid_sig) {
|
|
49 | 50 |
return; |
50 |
} else if (targets.signed) {
|
|
51 |
} else if (targets.policy) {
|
|
51 | 52 |
/* Redirect; update policy */ |
52 | 53 |
targets.target = targets.target2; |
53 | 54 |
delete targets.target2 |
54 | 55 |
} |
55 | 56 |
|
56 |
let redirect_url = targets.base_url + targets.sig; |
|
57 | 57 |
let [pattern, settings] = query_best(targets.base_url); |
58 | 58 |
if (!pattern) |
59 | 59 |
/* Defaults */ |
60 | 60 |
settings = {}; |
61 | 61 |
|
62 | 62 |
const policy = {allow: settings.allow, nonce: gen_nonce()}; |
63 |
const policy_string = encodeURIComponent(JSON.stringify(policy)); |
|
64 |
const sig = gen_unique(policy_string + targets.base_url); |
|
63 | 65 |
|
64 |
redirect_url += encodeURIComponent(JSON.stringify(policy));
|
|
66 |
let redirect_url = targets.base_url + '#' + sig + policy_string;
|
|
65 | 67 |
if (targets.target) |
66 | 68 |
redirect_url += targets.target; |
67 | 69 |
if (targets.target2) |
... | ... | |
73 | 75 |
function inject(details) |
74 | 76 |
{ |
75 | 77 |
const targets = url_extract_policy(details.url); |
76 |
if (!targets.policy)
|
|
78 |
if (!targets.valid_sig)
|
|
77 | 79 |
/* Block unsigned requests */ |
78 | 80 |
return {cancel: true}; |
79 | 81 |
|
80 | 82 |
const rule = csp_rule(targets.policy.nonce); |
81 | 83 |
|
82 |
var headers; |
|
84 |
var headers = details.responseHeaders;
|
|
83 | 85 |
|
84 |
if (targets.policy.allow) {
|
|
86 |
if (!targets.policy.allow || is_mozilla)
|
|
85 | 87 |
/* |
86 |
* Chrome doesn't have the buggy behavior of repeatedly injecting a
|
|
87 |
* header we injected once. Firefox does and we have to remove it there.
|
|
88 |
* Chrome doesn't have the buggy behavior of caching headers
|
|
89 |
* we injected. Firefox does and we have to remove it there.
|
|
88 | 90 |
*/ |
89 |
if (is_chrome) |
|
90 |
return {cancel: false}; |
|
91 |
|
|
92 |
headers = details.responseHeaders.filter(h => !is_our_header(h, rule)); |
|
93 |
} else { |
|
94 |
headers = details.responseHeaders.filter(h => !is_csp_header(h)); |
|
91 |
headers = headers.filter(h => !is_csp_header(h)); |
|
95 | 92 |
|
93 |
if (!targets.policy.allow) |
|
96 | 94 |
headers.push({ |
97 | 95 |
name : header_name, |
98 | 96 |
value : rule |
99 | 97 |
}); |
100 |
} |
|
101 | 98 |
|
102 | 99 |
return {responseHeaders: headers}; |
103 | 100 |
} |
common/misc.js | ||
---|---|---|
35 | 35 |
function get_secure_salt() |
36 | 36 |
{ |
37 | 37 |
if (is_chrome) |
38 |
return browser.runtime.getManifest().key.substring(0, 36);
|
|
38 |
return browser.runtime.getManifest().key.substring(0, 50);
|
|
39 | 39 |
else |
40 |
return browser.runtime.getURL("dummy").substr(16, 36);
|
|
40 |
return browser.runtime.getURL("dummy"); |
|
41 | 41 |
} |
42 | 42 |
|
43 | 43 |
/* |
... | ... | |
107 | 107 |
/* Extract any policy present in the URL */ |
108 | 108 |
function url_extract_policy(url) |
109 | 109 |
{ |
110 |
var policy_string; |
|
110 | 111 |
const targets = url_extract_target(url); |
111 |
const key = '#' + get_secure_salt(); |
|
112 |
targets.sig = key + gen_unique(targets.base_url); |
|
113 | 112 |
|
114 |
if (targets.target && targets.target.startsWith(key)) { |
|
115 |
targets.signed = true; |
|
116 |
if (targets.target.startsWith(targets.sig)) |
|
117 |
try { |
|
118 |
const policy_string = targets.target.substring(101); |
|
119 |
targets.policy = JSON.parse(decodeURIComponent(policy_string)); |
|
120 |
} catch (e) { |
|
121 |
/* TODO what should happen here? */ |
|
122 |
} |
|
113 |
try { |
|
114 |
policy_string = targets.target.substring(65); |
|
115 |
targets.policy = JSON.parse(decodeURIComponent(policy_string)); |
|
116 |
} catch (e) { |
|
117 |
/* TODO what should happen here? */ |
|
118 |
} |
|
119 |
|
|
120 |
if (targets.policy) { |
|
121 |
const sig = gen_unique(policy_string + targets.base_url); |
|
122 |
targets.valid_sig = targets.target.substring(1, 65) === sig; |
|
123 | 123 |
} |
124 | 124 |
|
125 | 125 |
return targets; |
content/main.js | ||
---|---|---|
100 | 100 |
|
101 | 101 |
if (!is_privileged_url(document.URL)) { |
102 | 102 |
const targets = url_extract_policy(document.URL); |
103 |
targets.policy = targets.policy || {}; |
|
104 |
const nonce = targets.policy.nonce || gen_nonce(); |
|
105 |
|
|
106 |
if (targets.signed) |
|
103 |
if (targets.policy) { |
|
107 | 104 |
if (targets.target2 !== undefined) |
108 | 105 |
window.location.href = targets.base_url + targets.target2; |
109 | 106 |
else |
110 | 107 |
history.replaceState(null, "", targets.base_url); |
111 |
|
|
108 |
} |
|
109 |
|
|
110 |
targets.policy = targets.valid_sig ? targets.policy : {}; |
|
111 |
|
|
112 |
const nonce = targets.policy.nonce || gen_nonce(); |
|
112 | 113 |
start_activity_info_server(); |
113 | 114 |
handle_page_actions(nonce); |
114 | 115 |
|
Also available in: Unified diff
Revamp signatures and break header caching on FF
Signatures, instead of consisting of the secure salt followed by the unique
value generated from the URL, are now the unique value generated from the
policy value (which will follow them) succeeded by the URL.
CSP headers are now always cleared on FF, regardless of whether the page
is whitelisted or not. This means whitelisting takes effect on page reload,
rather than only when caching occurs. However, it obviously presents security
issues; refinment will occur in a future commit.