Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[webapp] Ensure webapp integrity #6

Open
aguinetqb opened this issue Oct 6, 2021 · 5 comments
Open

[webapp] Ensure webapp integrity #6

aguinetqb opened this issue Oct 6, 2021 · 5 comments

Comments

@aguinetqb
Copy link
Contributor

Ticket to track ideas/advances on that known limitation.

@aguinet
Copy link
Collaborator

aguinet commented Oct 17, 2021

I've done a PoC here: aguinet/mattermost-webapp@04e50ef

The overall idea assumes that we have a Page Integrity-like plugin who verifies that, for a given domain, only signed/known pages are rendered in the browser (something Page Integrity doesn't do AFAIK, but should be doable). This allows for:

  • a "root of trust" of the web application
  • not rendering pages that are unknown (in the attack scenario where the attacker would have placed a malicious HTML/JS on the server and try to have the user load it in the browser where mattermost is running)

With this in mind, we do two things in the aforementioned commit to have a complete verification of the mattermost webapp:

  • use the Webpack Sub Resource Integrity (SRI) plugin while building the MM webapp. This has the effect to use SRI on every javascript loaded by the app. Combined with the root of trust given by the plugin above, we can verify almost the whole mattermost app
  • the only remaining things that doesn't have SRI in this setup are the plugins' webapp. To achieve this, we precompute the hashes these plugins are supposed to have per version (in this repository: https://github.com/aguinet/mattermost-sri ) , and query it from the webapp to add the SRI attribute to the created script element (done while loading a MM plugin).

This solution has interesting advantages:

  • it is completely transparent for both administrators and users, as long as the plugins' SRI are declared in https://github.com/aguinet/mattermost-sri . This could be something MM provides itself based on the marketplace data for instance.
  • it does not need a complex signing signature scheme

The main drawbacks is that plugin developers that want to try their develop plugins would have troubles. This means two versions of the webapp should be built: one for developers, one for production (potentially a non acceptable burden).

There might be another solution that would make this drawback disappear: if the Mattermost webapp includes by default the Webpack SRI plugin (and nothing else), then a dedicated Mattermost webapp integrity browser plugin could do the plugin-specific integrity attribute injection.

@jupenur
Copy link

jupenur commented Oct 18, 2021

The overall idea assumes that we have a Page Integrity-like plugin who verifies that, for a given domain, only signed/known pages are rendered in the browser (something Page Integrity doesn't do AFAIK, but should be doable).

Page Integrity doesn't provide this, or anything else particularly useful really. It might be doable but you should be aware of the pitfalls, like service worker caches.

a "root of trust" of the web application

I suppose this assumes you can just sign the root.html document and have everything else fall back on that via SRI. That's not really the case though, because even though mattermost-server is mostly a SPA, there are other HTML pages that it needs to occasionally render, error pages for example.

not rendering pages that are unknown

Because of the above, this will break things in unexpected ways unless server development is carefully aligned with e2ee plugin development.

use the Webpack Sub Resource Integrity (SRI) plugin while building the MM webapp. This has the effect to use SRI on every javascript loaded by the app.

This only applies to root.html, not the other HTML pages rendered by the app. SRI for those would have to be handled separately.

the only remaining things that doesn't have SRI in this setup are the plugins' webapp. To achieve this, we precompute the hashes these plugins are supposed to have per version (in this repository: https://github.com/aguinet/mattermost-sri ) , and query it from the webapp to add the SRI attribute to the created script element (done while loading a MM plugin).

This is assuming plugins only have client-side code in the bundle declared in the manifest and nowhere else. That's not the case for a lot of plugins. For example, many plugins provide integrations with third-party applications and authorize the user via an OAuth flow. OAuth flows often use additional stand-alone HTML pages to avoid unloading the main Mattermost chat tab. And those HTML pages obviously cannot be secured using SRI; there would have to be a mechanism to handle them in the browser extension.

Some plugins also load JavaScript in the main app dynamically in a way that wouldn't be covered by SRI. For example the Jitsi plugin, one of the plugins you already included in the PoC repo, needs to load a Jitsi API from a separate file. And even though that could be covered with a custom SRI setup, there's also a "compatibility mode" where the API is actually loaded from the external Jitsi server.

it is completely transparent for both administrators and users

It's not super clear to me what you mean by "transparent" here. If it's an essential property, maybe you can elaborate a little?

The main drawbacks is that plugin developers that want to try their develop plugins would have troubles. This means two versions of the webapp should be built: one for developers, one for production (potentially a non acceptable burden).

This is a non-issue. Mattermost already has a developer mode where it relaxes some security guarantees similar to what's needed here. The existing developer mode could be easily extended to support this use-case.


The biggest open question here is the design and implementation of the integrity extension. There are a lot of potential pitfalls, and it's not obvious to me that a secure implementation is necessarily even possible. There are questions around of how signatures are calculated and distributed, how signing keys are distributed and verified to be trustworthy, how the blocking is done technically, who maintains and distributes the plugin etc. Page Integrity provides some ideas but nothing that could be used as-is since it's terribly insecure itself.

@aguinet
Copy link
Collaborator

aguinet commented Oct 22, 2021

Thanks for your answer! Comments below:

The overall idea assumes that we have a Page Integrity-like plugin who verifies that, for a given domain, only signed/known pages are rendered in the browser (something Page Integrity doesn't do AFAIK, but should be doable).

Page Integrity doesn't provide this, or anything else particularly useful really. It might be doable but you should be aware of the pitfalls, like service worker caches.

a "root of trust" of the web application

I suppose this assumes you can just sign the root.html document and have everything else fall back on that via SRI. That's not really the case though, because even though mattermost-server is mostly a SPA, there are other HTML pages that it needs to occasionally render, error pages for example.

We could make a less "strong" model, that would be: if the page isn't signed, it is rendered but with javascript disabled?

use the Webpack Sub Resource Integrity (SRI) plugin while building the MM webapp. This has the effect to use SRI on every javascript loaded by the app.

This only applies to root.html, not the other HTML pages rendered by the app. SRI for those would have to be handled separately.

True. If these pages doesn't really need javascript, maybe we could be fine (see above).

the only remaining things that doesn't have SRI in this setup are the plugins' webapp. To achieve this, we precompute the hashes these plugins are supposed to have per version (in this repository: https://github.com/aguinet/mattermost-sri ) , and query it from the webapp to add the SRI attribute to the created script element (done while loading a MM plugin).

This is assuming plugins only have client-side code in the bundle declared in the manifest and nowhere else. That's not the case for a lot of plugins. For example, many plugins provide integrations with third-party applications and authorize the user via an OAuth flow. OAuth flows often use additional stand-alone HTML pages to avoid unloading the main Mattermost chat tab. And those HTML pages obviously cannot be secured using SRI; there would have to be a mechanism to handle them in the browser extension.

Some plugins also load JavaScript in the main app dynamically in a way that wouldn't be covered by SRI. For example the Jitsi plugin, one of the plugins you already included in the PoC repo, needs to load a Jitsi API from a separate file. And even though that could be covered with a custom SRI setup, there's also a "compatibility mode" where the API is actually loaded from the external Jitsi server.

Indeed I assumed plugins were fairly self contained. What we could say here is that plugins could have, as a hint, something like an "sri" boolean in their manifest, stating that every loaded resources is properly validated thanks to sri. Then an administrator that want their users to be able to authenticate the mattermost webapp would only use these plugins.

Coupled with a browser plugin that would enforce only loading signed pages with SRI-enabled resources, it could do the job (at least in our case).

it is completely transparent for both administrators and users

It's not super clear to me what you mean by "transparent" here. If it's an essential property, maybe you can elaborate a little?

I meant the overhead for users is zero (they won't see the difference). Mattermost administrators would only have to take of loading "SRI-enabled" plugins.

The main drawbacks is that plugin developers that want to try their develop plugins would have troubles. This means two versions of the webapp should be built: one for developers, one for production (potentially a non acceptable burden).

This is a non-issue. Mattermost already has a developer mode where it relaxes some security guarantees similar to what's needed here. The existing developer mode could be easily extended to support this use-case.

Good to know, thanks!

The biggest open question here is the design and implementation of the integrity extension. There are a lot of potential pitfalls, and it's not obvious to me that a secure implementation is necessarily even possible. There are questions around of how signatures are calculated and distributed, how signing keys are distributed and verified to be trustworthy, how the blocking is done technically, who maintains and distributes the plugin etc. Page Integrity provides some ideas but nothing that could be used as-is since it's terribly insecure itself.

Mostly agree on this part. Using Github as a "root of trust" could be good enough for a lot of use cases, with the plugins gathering signatures from the various release pages (as I did in the PoC for plugins). That's one idea, there are tons of others :)

@jupenur
Copy link

jupenur commented Oct 22, 2021

We could make a less "strong" model, that would be: if the page isn't signed, it is rendered but with javascript disabled?

There is an extension API to disable JavaScript (chrome.contentSettings) but it only works in Chrome and only allows scoping settings to full origins or wider, not individual pages.

@aguinet
Copy link
Collaborator

aguinet commented Oct 23, 2021

We could make a less "strong" model, that would be: if the page isn't signed, it is rendered but with javascript disabled?

There is an extension API to disable JavaScript (chrome.contentSettings) but it only works in Chrome and only allows scoping settings to full origins or wider, not individual pages.

The NoScript Firefox plugin manages to do this on a per (sub)domain basis, so I'd say that should be doable?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants