firewall.js creates an <iframe>
that blocks networking and downloading.
var x = document.createElement("iframe");
document.body.appendChild(x);
var f = FirewallJS(x);
f.onblock = function(e) {
console.log("Blocked access to:" + e.data);
};
f.load("<script>new Image().src='http://www.google.com/'</script>")
firewall.js is very new and incomplete: There are a lot of ways that JavaScript can be used to perform a network request, a lot of different subtly incompatible web browsers, and there's only about 30 test vectors at the moment, covering:
- Basic html tags like a, link, audio, video, images and script
- Complex browser interaction like innerHTML and events
- JavaScript API like location, sendBeacon and xmlhttprequest
- CSS/Stylesheets inline, parsed, and programmatic
- SVG
Ideally, a firewalled script will not be able to detect firewall.js
Suggestions and contributions are welcome.
An alternative to the "block all" default is a whitelist/blacklist mode.
var f = FirewallJS(x);
f.whitelist([ "http://good.example.com" ]);
will permit resources on good.example.com
while:
var f = FirewallJS(x);
f.blacklist([ "http://evil.example.com" ]);
f.whitelist([ "http://*.example.com" ]);
will permit resources on good.example.com
and whatever.example.com
but not evil.example.com
.
The blacklist and whitelist (if used) must be supplied before loading the content.
Another alternative mode is to simply monitor/log all of the URL requests instead of attempting to block them.
var f = FirewallJS(x);
f.monitor(true);
Safari can't intercept changes to innerHTML
and element attributes, so a MutationObserver
is used instead.
This means that network operations might not be blocked, although at least they can be detected and the iframe shutdown.
Node.JS is required to build the script and its test cases.
npm install
To build firewall.min.js
and tests.html
run:
npm run build
Tests need to produce network activity; a full-featured test will send a signal to the test system. On Google Chrome, we can reliably detect network activity even if we can't block it, however if your test only works on another browser, you may need to add some instrumentation:
parent.postMessage("fail",'*')
if you have (or believe you have) performed network activityparent.postMessage("pass",'*')
if you are not performing network activityparent.postMessage("block",'*')
if you can detect (e.g. with an exception) that network activity has not occurred
If you cannot "detect" network activity, but can manually verify it (by using developer tools or tcpdump), mark as failed after the critical command -- using setTimeout and a small delay if necessary.
Look at sendbeacon.js, image.js and xmlhttpreqest.js for some simple examples, and a3.js for a more complex one.
After every change, rebuild the test system with:
npm run build
then verify the test by loading tests.html
in a web browser. Note you can load a specific test by opening tests.html#
testname e.g. tests.html#style
will load tests/style.js.
An Anti-Test will make sure that a test isn't blocked. If you have example code that should work but doesn't with firewall.js, you can contribute an Anti-Test to prevent further regressions.
parent.postMessage("anti",'*')
This should be done first. Look at anti_a.js for an example.
The firewall API is in firewall.js. It's responsible for getting the payload into the iframe, and interpreting errors.
The payload is in firewall/ and are assembled with the firewall API into firewall.min.js
using build.js.
webpack is used for dependency tracking, and uglifyJS is used
to minimise the chunks. Any module that doesn't define module.exports
is considered an "entry point" and will be loaded automatically,
so to have a module demand-loaded late for its side effects, you must set module.exports
to something.
firewall.js, and its product firewall.min.js
are redistributable under the LGPL v3 or any later version, and for the avoidance of doubt and confusion, are derived from the tests.
The intention is: if you use firewall.js in your own product, you are not required to make available the source code to your product unless you introduce the ability to protect against a new attack vector.