-
Notifications
You must be signed in to change notification settings - Fork 0
/
background.js
288 lines (252 loc) · 14.9 KB
/
background.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
// Default max-age 6 months in seconds
var max_age = "15570000";
//TODO: change log types to can do OR and show types of logs eg. show HSTS forced enabled, or forced disabled, or redirs ignored, redirs filtered, and/or. rather than info, debug, verbose, etc.; actually make the flagvar be what NOT to log, so a value of 0 would mean log everything, and any set bits mean don't log that type of logs.
//nope: get rid of the 'new' - nevermind: https://developer.mozilla.org/en-US/docs/Web/API/URL/URL
var logflags={};
var blockhttp=true;//true if to block HTTP request for ignored hosts, false allow; otherwise, if host is not in ignore list it will be forced HTTPS! or it will fail to load(ie. if site is http only). Or in case of redirect loops(http->https->http) then the last http is allowed or not based on this value.(and that host is virtually ignored for the duration of the request and thus allowed to http complete) - TODO: also, if this is false, always log those allowed requests to Console!
//Note: access the following console.log() messages by going chrome://extensions/ then clicking the 'background page' seen as 'Inspect views: background page' under this extension (HSTS Everywhere) then chosing the 'Console' tab.
//src: https://stackoverflow.com/questions/1125256/how-to-create-a-constant-in-javascript
function setConstant(obj, key, value) {//eg. setConstant(logflags, "NONE", 0);
Object.defineProperty(obj, key, {
enumerable: true,
configurable: false,
writable: false,
value: value,
});
}
//order doesn't matter, these are bit flips! when set, it will NOT show(on Console) that type of logs! (set them in 'nologbits')
//XXX: to toggle INFO flag, type in Console: nologbits ^= logflags.INFO (this will enable/disable showing of INFO logs)
setConstant(logflags, "NONE", 0xFFFFFFFF);
setConstant(logflags, "ALL", 0x0);
setConstant(logflags, "ERR", 0x1);
setConstant(logflags, "ERROR", 0x1);
setConstant(logflags, "WARN", 0x2);
setConstant(logflags, "INFO", 0x4);
setConstant(logflags, "VERB", 0x8);
setConstant(logflags, "VERBOSE", 0x8);
setConstant(logflags, "DEBUG", 0x10);
setConstant(logflags, "HTTPALLOWED", 0x20);//XXX: this one's kinda redundant since blockhttp==false causes it to be forcefully enabled(without actually being set)
setConstant(logflags, "HTTPBLOCKED", 0x40);
//set this in Console! eg. nologbits = logelevels.DEBUG
var nologbits = logflags.NONE;//don't log anything by default, I read somewhere in passing that it's slow and saved to disk by chromium...
nologbits ^= logflags.HTTPBLOCKED;//actually do log when http gets blocked! (auto logged when http is allowed already!)
function log(logtype, msg, forceshow=false) {//should never pass NONE level when calling!
lognone=nologbits & logtype;
//console.log(lognone+" "+logtype+" "+nologbits);
if ( (lognone == 0) || (lognone != logtype) || (forceshow) ) {
if (logtype & logflags.WARN != 0) {
console.warn(msg);
//TODO: add console.err or is it .error ? check and see!
}else{
console.log(msg);
}
}
}
function loghttpallowed(msg) {
loghttp( logflags.HTTPALLOWED, msg );
}
function loghttpblocked(msg) {
loghttp( logflags.HTTPBLOCKED, msg );
}
function loghttp(logtype,msg) {
log(logtype | logflags.INFO, msg, false==blockhttp);
}
function logverb(msg) {
log(logflags.VERBOSE, msg);
}
function logwarn(msg) {
log(logflags.WARN, msg);
}
function loginfo(msg) {
log(logflags.INFO, msg);
}
function logdebug(msg) {
log(logflags.DEBUG, msg);
}
//NB: I left these two in 'ignore' as samples, but since I'm planning on permanently using HTTPS-Everywhere with 'Block HTTP requests', I won't be doing any HTTP requests ever, and thus I can afford to also set 'includeSubDomains' below.
//This is a list of hosts for which to ignore HSTS (but if they specify HSTS in headers it will pass through to the browser! TODO: check if this is so!):
var ignore = [//TODO: ignore in http->https redir too!
"pastebin.com",
"arstechnica.com",
"imdb.com",
"www.imdb.com",
"static.sfdict.com",
"dictionary.com",
"www.dictionary.com",
"www.merriam-webster.com",
"www.w3schools.com",
"batteryuniversity.com",
"www.thinkwiki.org",
"www.rustacean.net",
"utf8everywhere.org",
"conf.nixos.org",
"lists.science.uu.nl",
"news.gmane.org",
"www.synonym.com",
"www.theguardian.com",
"ow.ly",
//TODO: allow http to ignored hosts! even when blockhttp is on! perhaps add another var, blockhttptoignored=false by default!
];
Array.prototype.clone = function() {
return this.slice(0);
}; //src: https://davidwalsh.name/javascript-clone-array
//The following is list of hosts for which to force disable HSTS(incl. subdomains), this takes precedence over the ignore list above; just beware that if you're visiting a subdomain of a host listed here, it will enable HSTS for it and its subdomains; ie. 'test.com' will be force-disabled, but since it redirs to www.test.com this one will be force-enabled(and any subdomains like *.www.test.com, if any, due to 'includeSubDomains' below)
var forceDisable = ignore.clone();
forceDisable = forceDisable.concat([ //src: http://www.w3schools.com/jsref/jsref_concat_array.asp
'test.com',
'www.w3schools.com',
]);
//TODO: should force http those that are in forceDisable and ignore lists AND if blockhttp == false! eg. going to https://www.w3schools.com/js/js_numbers.asp should force http by redirecting to http://www.w3schools.com/js/js_numbers.asp
//hashmap? src: https://stackoverflow.com/questions/8877666/how-is-a-javascript-hash-map-implemented
var redirloopdetect={}; //"hashmap"!
var cwr = chrome.webRequest;
//so, this part will force all http attempts to go through https ! and if they can't because no https server is listening well, too bad... you'll get a ERR_CONNECTION_REFUSED as usual.
//why did I need this? well, the links from Variable bindings from here, for example: https://github.com/carols10cents/rustlings#variable-bindings were all http and https-everywhere wasn't doing anything about them (whilst 'Block All HTPP requests' being set was stropping anything from loading - so, don't need https-everywhere anymore now, i suppose)
cwr.onBeforeRequest.addListener(
function(details) {
// console.log(details);
ishttp=false;
redirecturl = details.url;
if (redirecturl.substring(0,5) === "http:") {
ishttp=true;
hostn = new URL(redirecturl).hostname;
fullh = hostn+" (full: "+details.url+" )";
if ( ignore.indexOf(hostn) > -1 ) {
loghttp( (blockhttp?logflags.HTTPBLOCKED:logflags.HTTPALLOWED),
"Host in ignore list: '"+fullh+"' therefore redir to https not forced! And this request is "+(blockhttp?"NOT ":"")+"allowed! Type 'blockhttp=true/false' in Console to change!");
return { cancel: blockhttp };
}
if (details.requestId in redirloopdetect) {
if (redirecturl.substring(0,5) !== "http:") {
logwarn("Developer fail! this should always be 'http', it's: "+redirecturl);
alert("Developer fail! this should always be 'http', it's: "+redirecturl);
}
//done: maybe just redir back to http instead? and based on blockhttp allow or not
/*if (!blockhttp) {
//manually add to ignore list or else it will redirect indefinitely, since we'll try to https it again!
}*/
if (blockhttp) {
loghttpblocked("Cancelled http (id="+details.requestId+") redir-loop to '"+ redirecturl+"' (you might want to allow http or else this will never succeed!)");
// logdebug("Blocked http to '"+ redirecturl);
delete redirloopdetect[details.requestId];
}else{
loghttpallowed("Allowed http (id="+details.requestId+") to '"+ redirecturl);
}
return { cancel: blockhttp };//, redirectUrl: redirecturl };//HSTS being enabled and us not canceling this even tho it's a http request, will cause a https request to happen next!
}
details.url = redirecturl = redirecturl.slice(0,4) + "s" + redirecturl.slice(4);
logverb("ForcedHTTPS as: '"+redirecturl+"'");
} else {
logwarn("!!!Sneaked url: "+redirecturl);//XXX: if this happens, it's some serious issue afoot!
alert("!!! IMPOSSIBIRU! Congratulations, you've just discovered the impossible! This url '"+redirecturl+"' was not a http url but was caught by the http filter. The request is about to be stopped after you exit this, but make a screenshot first and show it around, since this probably means there's a bug in chromium!");
return { cancel: true }
}
return { redirectUrl: redirecturl } //XXX: but HTTPS-Everywhere with 'Block HTTP request' will still cancel it! because details.url it receives is the same http one!
}, {
urls: ["http://*/*"],
}, ["blocking"]);
cwr.onBeforeRedirect.addListener(
function(details) {
//we're here because https tried to redirect to http! but we don't know if we forcefully set https and then got redirected back to http! yet.
if (details.redirectUrl.substring(0,5) !== "http:") {
loginfo("Detected ignored redirect to '"+details.redirectUrl+"' from '"+details.url+"'");
return;
}
//ok, here, only redirects to http!
loginfo("Detected https->http Redirect to '"+details.redirectUrl+"' from '"+details.url+"'");
//eg. "Detected https->http Redirect to 'https://www.google.com/' from 'https://google.com/'"
//Sample url which tries to redirect indefinitely(but chromium ofc. stops it after like 11 redirs): http://www.imdb.com/title/tt0088847/
// console.log(details);
//XXX: but details.requestId is the same on each redirect!
//so maybe allow the redirect to happen one more time and then we know if it's in a loop!
if (!(details.requestId in redirloopdetect)) {
//flag only when the same url (except the protocol being different!)
//since we're this far in, we know that details.url is https and redirectUrl is http !
//XXX: to test this, go here: https://stackoverflow.com/questions/21747136/how-do-i-print-the-type-of-a-variable-in-rust/25413103#25413103 then click the link with the text 'Shubham's answer'
if (details.url.substring(5) === details.redirectUrl.substring(4)) {
logdebug("Flagging "+details.requestId+" redirect to: '"+details.redirectUrl+"' from '"+details.url+"'");
// return { cancel: true }//hopefully? just guessing! 'It does not allow you to modify or cancel the request.' src: https://developer.chrome.com/extensions/webRequest
// }else{
redirloopdetect[details.requestId] = true;//any value i guess
//and can't cancel it here! 'It does not allow you to modify or cancel the request.' src: https://developer.chrome.com/extensions/webRequest
} else {
logdebug("Not flagging "+details.requestId+" redirect to: '"+details.redirectUrl+"' from '"+details.url+"'");
}
}
}, {
urls: ["https://*/*"],//ok, this means the redir FROM this type of url, to any other type, is detected! eg. redir dest url can be http or https or anything else even!
});
cwr.onHeadersReceived.addListener(
function(details) {
thisage=max_age;
hostn = new URL(details.url).hostname;
fullh = hostn+" (full: "+details.url+" )";
force_disable=false
if (details.requestId in redirloopdetect) {
delete redirloopdetect[details.requestId];
//this means we need to force-disable the HSTS which we already set! or else it will redir loop! but we're here because we're allowing http request after a fail of such redir to https(because that site, eg. imdb, redir-ed us to http on its own!)
loginfo("BOO! "+details.requestId+" "+details.url);
//return { cancel: true };
force_disable=true
}
if ( forceDisable.indexOf(hostn) > -1 ) {
force_disable=true
logverb("Host in forceDisable list: "+fullh); //eg. https://pastebin.com
}
// ignored=false;
if ( ignore.indexOf(hostn) > -1 ) {
logverb("Host in ignore list: '"+fullh+"' therefore HSTS not forced!"); //eg. https://pastebin.com
// ignored=true;
if (! force_disable) {//forceDisable list takes precedence over ignore list!
return { }; //TODO: Does {} mean that no changes were perfomed to headers? so original headers are preserved? check online docs to see if this is so!
}
}
for (var i = 0; i < details.responseHeaders.length; i++) {
// console.log(details.responseHeaders[i].name);
if (details.responseHeaders[i].name.toLowerCase() === "strict-transport-security") {
/* if (ignored) {
//remove HSTS header if host is in ignored list; OR we can leave it and set max-age to 0 to disable HSTS...
details.responseHeaders.splice(i,1); //remove 1 item from index i; src: http://www.w3schools.com/jsref/jsref_splice.asp
} else {*/
logverb("HSTS already set in host's response headers: "+fullh+" "+details.responseHeaders[i].value); //eg. grc.com max-age=31536000; preload
if ( ! force_disable ) {
return { };
}
// }
}
}
if ( force_disable ) {
//How to turn off an already set(aka browser rememberes) HSTS setting? src: https://wordpress.org/support/topic/want-to-turn-off-http-strict-transport-security-hsts-header
thisage=0; //confirmed to work on chromium 49.0.2623.87 (Developer Build) (64-bit) via Query Domain (after manually having added it with the above 'Add domain', see chrome://net-internals/#hsts )
}
details.responseHeaders.push({
"name": "Strict-Transport-Security",
"value": "max-age=" + thisage + "; includeSubDomains" //src: https://www.chromium.org/hsts
//check HSTS status: under HSTS(while Capture is enabled!) then 'Query domain' from here chrome://net-internals/#hsts If "*_upgrade_mode: STRICT" then HSTS is on! How to interpret: https://security.stackexchange.com/questions/68883/checking-domains-hsts-status
//also note, if 'broken HTTPS' (eg. https://rustbyexample.com since it's hosted by github) then the above query will yield 'Not found'
});
logverb("Force-"+(force_disable?"disabling":"enabling")+" HSTS for: "+fullh+" "+details.responseHeaders[details.responseHeaders.length-1].value);
return {responseHeaders: details.responseHeaders};
},
{
urls: ["https://*/*"],//http-only will never send HSTS headers - so is the spec.!
types: ["main_frame", "sub_frame", "stylesheet", "script", "image", "object", "xmlhttprequest", "other",
"font", "ping",
//rest are not accepted: "xbl", "xslt", "ping", "beacon", "xml_dtd", "font", "media", "websocket", "csp_report", "imageset", "web_manifest" src: https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/WebRequest/ResourceType
//FIXME: catch behind-the-scenes too! eg. uBlock/uMatrix can, also they can fetch resources over http!
]
},
["blocking", "responseHeaders" ]
);
//this has no effect here:
/*
//src: https://github.com/nemrod/chrome-disable-f12/blob/master/extension/disablef12.js
document.addEventListener("keydown",
function keydownfunc(e) {
if (123 == e.keyCode) {
e.returnValue=false;
}else
e.returnValue=false;
}
, false);
*/