Code Monkey home page Code Monkey logo

webext-signed-pages's Introduction

Signed Pages

A browser extension to verify the authenticity (PGP signature) of web pages.

GitHub tag Mozilla Add-on Chrome Web Store

Overview

Why?

This extension was originally created to improve the security of the EteSync web app. One of the biggest issues with securing web applications is the fact that the app (JavaScript) is delivered to you every time you open the page. This means that a malicious (or compromised) web server could change the code to steal your supposedly client-side-only and secure data.

This extension solves this by verifying the code really came from the developer. While this doesn't protect you from a malicious developer, it at least brings the security of the web app to a similar level to that of native apps.

How does it work?

Developers sign their web pages using their secure PGP key before uploading the pages to the server (for example, on their development machine). Users add a website's configuration (paths and matching publickey), if not already present. Then, every time the users access the website, the extension will indicate if the HTML pages are correctly signed, and thanks to subresource-integrity, also verify the integrity of external resources.

Usage

Installation

The official extensions represent the current stable release.

Opera users can enable Chrome extensions and then install the Chrome extension.

As a user

All you need to do is install the extension, and from its settings page, add patterns to match pages you'd like to verify, and their corresponding publisher's public key. The developers of those websites must have their pages signed for this extension to work.

Users with the browser extension configured will then see a green shield icon for verified pages, and a red one for pages with a bad or missing signature (assuming they were expected to have one).

For example:

Good signature

Example pages

You can try the following example pages to see how the extension behaves:

Install the extension and add the pattern and pubkey shown in the page from the extension's settings.

As a developer

You need to add a comment at the top of the html file (right after the doctype if exists) that contains the detached PGP signature of the content of the <html> tag after it has been minified with minimized with a specific set of settings.

As you can see, it's a bit involved, so we created a script that does all of this for you. All you need to do is make sure you have a comment at the top of the file that contains the special replace tag like in example.html.

And then just run, on a secure machine, preferably with a PGP key on a separate hardware token:

# Print the signed page to stdout
$ ./page-signer.js input.html

# Print the signed page to a file (can be the same as the input file)
$ ./page-signer.js input.html output.html

It's important that all of the external resources to the page (JS and CSS, whether hosted on the same server, or not) will have subresource integrity correctly set. This way you only need to sign the html page, and the rest will be automatically validated by the browser, ensuring that all of the scripts and styles used in the page are indeed what you expect.

A note on dynamic websites

The page-signer.js tool above was designed to work with static html files, meaning html files that are not generated on the fly by the server. The reason for that is that for the signing to be most effective, pages need to be signed by the author in advance, and can't be done dynamically by the server.

This is perfect for statically generated websites and blogs using tools such as Pelican and Jekyll, or web apps like that draw their dynamic content through JavaScript such as applications created with React, VueJS, Angular and Ember.

There are some workaronuds to dynamic websites work, by for example including dynamic content that doesn't matter like comments in an <iframe>, but those are quite involved and out of scope for this document.

Supported Sites

Adding support is easy. If you are a user and would like a website to be supported, please contact the site's owner and point them to this readme.

If your site already supports Signed Pages please consider adding the following badge (as a link to your settings) to let your users know about it.

Signed Pages Badge

List of websites that support Signed Pages:

Building

Setup the environment needed for this extension and page-signer.js:

npm install

To build the extension for development run:

npm run-script build

To build it for deployment run:

npm run-script package

Technical details

On Firefox, this extension relies on webRequest.filterResponseData which lets it intercept the request and sign the page as transferred by the server, so it can verify the page exactly as sent.

Unfortunately, other browsers don't support this API yet, which means we have to resort to a less clean way of doing it. On other browsers, the extension waits until the DOM has loaded, and just before scripts have been executed to get document.documentElement.outerHTML. This means that on these browsers it only has the ability to verify the <html> tag and its contents.

What makes matters even worse is that browsers don't return the html as delivered, but may mangle it a bit, which means we have to transform the content into a canonical form before signing (and verifying). This forces us to use a minifier on the html.

Be aware that the minifier may have bugs that can cause a page to pass verification while being different! Unlikely, but possible, so watch out for minifier bugs.

Since the same signature needs to work on all browsers, we unfortunately have to minimise the html on Firefox too. This workaround will be removed once the aforementioned filterResponseData is implemented across browsers.

Potential attacks

  • This extension rejects pages with <script> tags outside of the <html> tag, so while this could have potentially been an issue, it has been mitigated.

The whole page, other than the doctype is validated in Firefox since it implements browser.webRequest.filterResponseData.

Other browsers are implemented slightly differently and may be exposed to similar attacks.

Known issues

  • On Firefox, you may need to refresh a page for the first time after installing the extension if the page was already in cache.

FAQ

Can I sign only parts of the page? Or only external JavaScript?

No you can't. Verifying only part of a page would be very useful. One could, for example, automatically verify authorship of blog posts. Unfortunately, because of HTML's flexibility, it's not possible.

Let's first take a look at verifying only external JS. The main problem would be that the page itself could have javascript there (or additional unverified external javascript) that can run and do whatever it wants, so this is obviously not safe. Let's continue with this use case, and just disallow any embedded scripts in the page, or external, unverified javascript. We now have a problem that a malicious server could for example have a div overlay that when clicked triggers javascript. Let's assume for the sake of discussion that we don't require any inline JS and that, so we can just block all of the inline JS using CSP.

Even with the above solved, an attacker can still for example, modify buttons to be forms, rather than AJAX requests (of if already a form, change the target), which means the data will be sent to an attacker controlled server. This is obviously not good. Another thing an attacker could do, is change your announcements, bitcoin addresses, PGP keys, and a variety of other parts. OK, so allowing changing the HTML is a bad idea.

What about CSS? This can also be problematic! An attacker can hide important text, replace text with malicious text (think again, bitcoin, PGP keys and etc) and probably more issues that I haven't considered.

This is why I verify the whole page an suggest using SRI even for CSS. HTML is very complex, so the attack surface is very wide.

Attribution

Icons are based on the following icons:

webext-signed-pages's People

Contributors

dependabot[bot] avatar tasn avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

webext-signed-pages's Issues

Sign git commits & releases

This project is about signing web pages, but how can we trust the source here came from you (including the whether the bundled PGP keys, etc, are correct) and GitHub does not serve us modified stuff here. The solution is easy, IMHO, because git has a built-in solution:

You should consider signing git commits & releases.

Extension no longer works in Firefox 118

Hi @tasn,

I love the idea of this extension and wanted to adopt it for some of my projects, however I couldn't get it to work on the latest Firefox. I'm also seeing some weird behaviour on Chrome where it would tell me some pages were signed even though they shouldn't be, which I suspect is because the icon is simply not updated.

I took a quick peek, and I found some issues with the current manifest.json. Then, when attempting to build this locally, some issues popped up due to a lot of the dependencies getting really old.

I suggest to bump the manifest to be compatible with V3, and also rework / bump the extension dependencies themselves.

Next, I was unable to get the icon to work at all with page_action, probably because it's missing the correct show_matches configuration, but got the extension to work again with a simple browser_action.

I can create a PR which bumps all the dependencies to the latest*, and attempts to get this working on the latest browsers again. Let me know if that works for you,and if you have any thoughts.

Thanks

See also:

*One tricky part is that I think the "minimize" extension hasn't been touched in years, and could use an up-to-date alternative, but for that the minification logic should be checked in depth to not break the old behaviour.

Bubled PGP keys: How to prevent forgery? How do you verify PRs?

I've made another project aware of this extension and one question about the bundled PGP keys came up:

Furthermore, how would they prevent an attacker from simply sending a pull request that replaces the public key, claiming to work at that company?

So how would you do?

This is more about the process when/how you accept a PR? IMHO, you need to verify the ownership of the domain (e.g. just let them sign some random stuff you give them and say they should put that on the website), don't you? At least that would be the minimum…
Of course, it defeats the whole purpose as an attacker, who overtook a server, can also verify ownership of the domain, so you have to take this into account and deliberately delay the PR merging or so, and probably need to let them verify it again, so that it was not a "short time the server was overtaken".

Or you require PRs to be official GitHub accounts of that company?
Or some stuff involving Keybase?

Show a special icon for pages that are signed which don't have rules set

This is somehow related to issue #1 in making this extension even easier to use.

The idea is that if you have the extension installed, and you visit a website that signs it pages, but you don't have configured, it'll show you an icon indicating of that and suggesting you configure the website. With potentially automatic configuration (a sort of trust on first use) if you just click on the icon.

webext-signed-pages does not work with utf8 pages

I love the concept of web page signing, and this implementation is super cool. I can get it working on Firefox 125.

However, verification does not work on pages with utf8 content.

Per my tests, it seems to fail on the firefox side, the signature is OK on the server side.

Disable JS for pages with a bad/missing signature

Heya! I'd like to propose that the signed pages web extension should disable JS when a page fails a signature check. This would help protect against the following attack

  1. User logs into the (uncompromised, valid sig) web app, goes about their business, and then closes the app without logging out
  2. The web app is compromised by a malicious attacker interested in user data
  3. The user returns to the web app.
  4. The user's browser has kept their credentials, but not cached the page, and so loads the app from the server
  5. The server sends a malicious web app
  6. The (now malicious) web app uses on-load JS to immediately upload the user's local storage to the attacker's server BEFORE the user can react to the page having a failed signature.
  7. User's credentials are now compromised until the user invalidates them.

This would also add an additional measure to prevent unobservant users who fail to notice an invalid signature from providing credentials to a compromised page.

Note: If this behavior is already present, I'd instead like to use this space to request that this information be added to the extension's README

Thanks for your time!

Support for DNS TXT record with master signing key for domain

I was planning to write almost exactly this plugin when I found yours.

My original idea differed a little. I was thinking to place a public key into DNS that was used to sign any public key on the domain. I was also thinking an SSH client model for saving public keys, eg:

  • you have never seen this public key before, do you want to save it?
  • this public key changed from what you saved? Continue?
  • Warning, the public key for this page is not signed by the key specified in the domain

I know this raises a lot of thorny issues code-wise, but I don't think they are insurmountable. Is this a direction you are interested in taking this plugin?

How is PGP better than EV SSL?

I'd like to know, why should any site owner bother with creating a PGP key for a site when there is already a widely known and used alternative - Extended Verification SSL certificates.

The only pros for this I see are:

  • no monthly fees
  • no certificate authorities to trust (for this feature specifically)

but I don't think these reasons are good enough for most sites to care. I believe it could be a good fit to .onion sites though.

Support multiple public keys per website

At the moment, it's one pubkey per one pattern. You can workaround this by providing equivalent but slightly different regex patterns, but it's better to just support them natively.

Verify JavaScript only

At PrivateBin (a secure pastebin with e2e crypto), we have a use case, where we want to allow server admins to customize the design/layout of the page, and only want to verify the integrity of the JavaScript, which may be the only thing, which can actually be malicious.

See PrivateBin/PrivateBin#138 for details.

So we use SRI, but a good idea would be to verify the JavaScript/SRI hashes only. Would that be possible?

Code Verify browser extension + manifest

Facebook is promoting a similar concept to this extension:

Eventually reusing their idea of a manifest for valid site sources is useful for EteSync et al.?

This is to say, this repository has not received push activity in more than one year, which is rather uncommon for security-relevant software like this. This may help in getting the same feature from a supported codebase instead.

-- is not allowed in HTML comments

When following the HTML comment syntax correctly -- is not allowed in the comments. At least Firefox complains about this when viewing the site in the source code mode.

So maybe you can omit it? (and just internally add it, or so)

Add support for Minimizer Version

Currently the system takes the raw content and runs it through a Minimizer to create a canonical form before using it for signature creation or verification . This makes the signature highly dependent on the exact workings of the Minimizer. Version changes of the minimizer could change the output subtly, rendering signatures using the old minimizer invalid.

By including a version number (and/or method of canonization) in some kind of configuration block, the verifier can choose the appropriate version.

The configuration block could also include the version of the webpage for #13.

Show popup on bad/missing signature

If there is a signature error, can you somehow show a popup? Or safely inject a popup into the website? (Safely, so that the website cannot remove it.)

Or maybe the easiest thing would be to do it like adblockers sometimes do (uBlock Origin e.g.) and redirect to an own internal site of the add-on, which then displays a warning.

Add release support/downgrade protection

Your not very low aim is to provide the same security to web versions of software, as for native desktop apps.

Okay, you verify the integrity (and to some extend also authenticity) of the software, fine.

Now, however, desktop apps with usual downloads also have another advantage here.

You can edit a released version of the software and know this is secure. Who you can subsequently download it.
Audits can this be made for specific versions of a software.

Also you can find a vulnerability in a specific version. This is then fixed in the next version and the software is updated.
You can prevent downgrade attacks NY just checking the version of the new software is newer than the old one.

All this requires versions. That is something web applications usually do not have.
This, this add-on e.g. does not prevent downgrade attacks.

in practise

Say version 1.0.0 has a vulnerability. Now v1.0.1 is released and the server admin updates the software.
When the server admin now wants to attack a user, they can just serve the old v1.0.0 once and this exploit the known vulnerability.

protection

I suggest to implement some version checking in this add-on.
Maybe use the manifest.json or something like this and add a custom version field. Or better include it in the (signed) HTML file. When a signed website is visited, this add-on should then just record (save) the version number of the software.
When the website is visited the next time, it just compares the version number and shows a critical error when the version number is lower than the saved old one.
That's it, theoretically wore easy! (Of course you need to use a very good library for version comparison or so, because when this cab be circumvented your whole protection can be circumvented.)

And as an additional protection, it may require that a version number is present. The software creator had to include it, but IMHO they should do so, as this is an attack you have to consider.
For the start, you could make this an optional thing. Or just require it, AFAIK not too many projects use this extension for now.😉

Breaks downloads

I've been investigating a Firefox issue and it turned out your addon was causing it. If I have to guess, you somehow do not close/finish a request when it is cancelled or so…

So here are the full details, STR and so on: https://bugzilla.mozilla.org/show_bug.cgi?id=1480745

This is a very annoying minor bug that you'll get annoyed about each time you see it. 😄

Verify that SRI is used for all elements ("require-sri")

I already mentioned this in another issue, but here again.

You can use "require-sri" in the CSP to instruct recent browser to load stuff only with sri. (Or was it still a draft??)

You should suggest this to software devs using this add-on. That should include it as a meta-tag in the source, so it is covered by the signature (in contrast to http headers).

So how should that protect against attacks?

So let's take https://client.etesync.com/ as an example. Our thread model is a malicious server.

  1. You run https://client.etesync.com/.
  2. You sign the page with a PGP key you own.
  3. We verify each page we get from https://client.etesync.com/ and assure it is signed by your key.

So when the server (i.e. you) turns evil, they can just sign each page anyway. we verify it and that's it.

So maybe you should clarify that this nylon helps if:

  1. the private key is stored securely (i.e. not on the server!)
  2. the owner of the private key is not an attacker/evil in your thread model
  3. i.e. that only helps against a server compromise, not against adding "backdoors"

Also it helps when:
4. you want to make sure self-hosted installations correspond to the official hosted one.

To prevent against a rogue server owner you would additionally have to verify that the minified code (or, the signature) corresponds to the one hosted at GitHub, respectively, that the signature is published on GitHub and it signs a valid release. That would be a hard task.

Chrome store extension doesn't think client.etesync.com is good.

I get a red x using both Chrome and Chromium.
The Firefox extension says the site is signed properly.
I verified the extension works in general with the good/bad pages.

I addition, loading the unpacked extension in ungoogled chromium complains about an error.

Screen Shot 2019-03-19 at 3 21 45 PM
Screen Shot 2019-03-19 at 3 20 33 PM

Add an easier way to add configuration from a website

At the moment we ask users to manually add the pattern and the publickey. It'd be much better to just let them paste a link to a json config file which we'll print the sha256sum of, so users can be sure its what they expect.

This will be better for the users without compromising security.

Thoughts on "HTML Signing Profile"?

https://www.w3.org/2007/11/h6n/

I'm not a fan of the entire thing, but, it does seem like it could possibly be safer:

at the moment, there is 3 separate parsers all working on the same file: the minifier parser (to be removed once chrome is fixed though), the browser parser, and the parser to find the signature. If there is a mismatch for any of those, then that's an opportunity for something to potentially be snuck through

for something based on HTMLSP, the process would instead be: the HTML page has a link to a file providing proof of the integrity of the document, the file signing the document explicitly states the URL(s) and their hashes, and then that list of urls and hashes gets signed.

As a result, only 1 parser is doing work (The browser's parser can be used to find the link, without executing javascript or fetching resources via DOMParser, however this might cause other issues), downsides of this are that it can cause slowdown (probably too much slowdown without http2 / server-push)

Another thought would be to try and embed a signature into the HTTP headers, though there might not be enough space

obviously I wouldn't suggest such suffering as using XMLDSIG as they suggest, if JSON were to be picked instead, making a canonical version of the SigningInfo block would be a whole lot easier.

And it would obviously complicate the implementation a lot

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.