Note: This library isn't finished yet. Please check back later.
This is a JavaScript library to protect the HTML, JS and CSS in web apps from tampering by malicious servers or developers. It does this by installing some code in a Service Worker, which checks the code every time you open the web app. In effect, this makes it Trust on First Use.
Most web apps inherently require you to trust the servers and developers (for example, because they send your data to the server). However, some do not. This can either be because they store and process your data entirely on the client, in JavaScript, or because data is encrypted on the client before it is sent to the server. This library is meant for those web apps.
When you open a web app, all of its code is delivered by its web server. It is fairly trivial for the operators of that server to one day decide to serve you code that does send your data to the server, or doesn't encrypt it before doing so. Similarly, a hacker which compromised the server can do the same. Even worse, a malicious developer could target just one or a few users, and serve them malicious code. That would be almost impossible to detect.
Merely signing the code, and delivering public key signatures together with the code which are checked against a public key, does not solve the last attack mentioned in the previous paragraph. After all, the developers could write some malicious code, sign it, and then deliver both to a target user.
This library is a building block, just as encryption is a building
block. It does not, by itself, "make your web app secure". In
particular, it does not attempt to verify that all code in the web app
is checked, and that it does not eval()
other, untrusted code, etc. To
check that, you should make use of a Content Security Policy.
As a general rule, if all code in your web app comes from your own server, or from a third-party server while using Subresource Integrity, and you use an appropriate Content Security Policy to verify all that, and all the client-side code from your server is on GitHub and verified by this library, then you're set.
Note: This library is experimental and subject to change (as is its API). To a lesser extent, so is the Service Worker API and its support by browsers. Therefore, if you decide to use it, update this library often to stay up-to-date with security patches.
Example app:
Example app running on Heroku (code on GitHub).
Installation:
-
Include this repository under your project and copy
serviceworker-stub.js
to the root of your project:git submodule add -b master https://github.com/airbornio/signed-web-apps.git cp signed-web-apps/dist/sw/serviceworker-stub.js .
-
The following code should be included on every page of your web app (even 404 and other error pages). If you don't, an attacker could send users to a page without it, and the library would have no way of warning users of any malicious code on the page.
<script src="/signed-web-apps/dist/client.js"></script> <script> let swa = new SWA({ url: '/serviceworker-stub.js', }); swa.addEventListener('urlChecked', event => { let data = event.data; if(data.msg === 'signature_matches' || data.msg === 'response_unchanged') { // SUCCESS } else { // data.msg is 'signature_mismatch' or 'signature_missing' or 'network_error' alert(data.msg + ': ' + data.path); } }); swa.addEventListener('error', event => { // event.code is 'sw_not_supported' or 'sw_failed' alert(event.code + ': ' + event.message); }); </script>
-
Create a file called
serviceworker-import.js
in the root of your domain. This file will (1) import other parts of the library and (2) tell the library where on GitHub to find your files.Generate your configuration code here and copy+paste it to that file.
-
Make sure that your server serves
Last-Modified
headers that correspond to either the date when you last pushed files to your server, or the date when the specific file changed on your server (the latter may lead to an increase in GitHub API requests in some cases, though).The default configuration from the previous step assumes that you:
- Push to GitHub before you push to your server, and that the
date in the
Last-Modified
header is later than when you pushed to GitHub. - Always push to your server within a day of pushing to GitHub.
It's probably a good idea to set up a
production
branch for this purpose. - Don't push an old commit to your server. If you want to rollback your server to an older version, it's probably best to create a revert commit and push it to GitHub and your server.
- Push to GitHub before you push to your server, and that the
date in the
-
Update often. (Please see the note above the installation instructions for the reasons why.) Preferably add this to your install or build script:
git submodule update --remote cp signed-web-apps/dist/sw/serviceworker-stub.js .