Is it secure for pods to serve html files?

EDIT: created a solid specification issue here: Security considerations for serving html files · Issue #514 · solid/specification · GitHub

TL;DR

I think it’s a security issue that pods serve html files, as the web treats html files from the same origin with the same security permissions. Solid pods breaks the base assumption of web security that files from the same origin are equally trustworthy. Thus I’d like to discuss if this should be disallowed, either in the spec or just removed from the implementations.

Introduction

Currently pods serve html files when visiting them in the browser. For instance, going to pod.example.org/public/file.html will render the html file in the browser. My question is, is this secure? And if yes, under what conditions?

I didn’t think it completely through, but my gut feeling is that it is not secure, always leaving a security issue, especially if html files there use sensitive data.

Main security issue

The main problem is, that the web often assumes applications on the same origin to share a security context. There is a limit how applications can interact with applications on different origins, but if the application is on the same origin it is much easier with less security boundaries. (See eg Same-origin policy - Web security | MDN)

This does not map to Solid pods. Here, we can give one app access to /foo and another app access to /bar which share an origin, but can be trusted on different levels. For instance it could be /drawing and /banking, where we likely want the drawing app not to interfere with the banking application.

Examples

  1. LocalStorage: this is a storage that is shared within the same origin. The drawing app would be able to access stored data from the banking app.
  2. Cookies: By default cookies use SameSite: Lax which means they are not send cross-origin, but are sent on the same origin. Requests from the drawing app would include cookies from the banking app. A real case is this issue where the Drawing would be able to use the mashlib NSS session cookies.
  3. Iframes: Given an app /banking/index.html, a malicious drawing app /drawing/evil.html can include the banking app as an iframe and access the contents of it. A real case is this issue where Mashlib accidentically gives html files it displays access to its window (EDIT: apparently mashlib manually fetches the html file, so it’s not a real case of this. But the idea is the same).
  4. Url history: the url is modifiable as long as the origin stays the same. Thus the app /drawing/index.html could use window.history.pushState('page2', 'Title', '/banking/index.html'); to trick the user into believing they are looking at the banking app.

In addition to these app-app issues, serving html files can also cause issues when only one evil application is present:

  1. Service Workers: a service worker can act as a proxy within it’s scope. The scope is everything within the same folder or subfolders (/foo/serviceWorker.js can intercept requests to /foo/**/*
    ). It requires substantial prerequisites, but when an attacker is able to install a service worker, they have limited control over all nested resources in the folder, regardless of their access control.

I’m pretty sure there are other cases, where it could be troublesome that apps of different trust-level are hosted on the same origin.

My current conclusion

Given these issues, I don’t think it is safe for pods to serve html files such that they are rendered by the user. For service workers, you could disable them with a CSP header (worker-src). For the other issues, I think it would require apps not to use any sensitive information. My guess is also, that there are more issues I didn’t think of yet, as Solid breaks a base assumption that web security relies upon, so maybe even the case where no sensitive information is handled causes security issues.

So I would consider to not serve html files, or to disable Javascript (and maybe more?) via the Content Security Policy header. With this the above issues should be migitated, but one can still use minimal html websites.

4 Likes

I would go with option C, that a new security model is required for handling executable code within pods.

Solid has already abandoned the single origin trust model completely by replacing CORS with an alternative authentication and authorisation system.

It’s JavaScript that is the issue, not HTML, and there are well established methods of sanitising HTML that can be applied both in read and write of HTML to eliminate untrusted code.

I would go further, that the vision of the read-write web now extends to JavaScript, and we need to provide a safe way to host executable code within a pod. There’s a long history of tackling this problem in other environments, including with executable permissions for files, opt-in trust of remote files, and sandboxing. You already note CSP too.

At the moment solidos in particular does nearly none of this (I think even IFrames are rather permissive), and historically that’s been fine because there’s not a huge number of apps, we trust them, and as far as I know none of them have written executable code to a pod yet.

We’re already started moving away from that with client IDs, which I understand will allow finer grained control over what data each app can access, and by extension what data executable code running on a pod might be able to access.

I’d read the examples provided as potential threats that don’t have mitigation yet.
It does look like blacklisting js and IFrames would prevent all of these?
Whitelisting specific features would likely be difficult (though not impossible), but probably a first step that can happen now already is informed consent - an app shouldn’t write executable code to a pod without explaining what capabilities it is using (and why?)

1 Like

Thanks for your comment.

First of a general remark: Regardless of my opinion on executable code on the pod, I think it is important to fix the current security issues even if there is no solution yet for executable code on the pod. As you said, I don’t think there are many applications using html files on the pod so it shouldn’t be much of an issue to sandbox/remove html files, but on the other side this decreases the attack surface quite a lot.

Yes, all of these depend on Javascript so a Content-Security-Policy: script-src none; should (to my current knowledge) prevent all of these (including the iframe one). There is also the sandbox CSP that I’d intuitively tend to to lockdown html files.

Yes, I agree that Javascript is at least the main security issue. I’m not sure if you could do malicious stuff without Javascript, I guess html is not sufficient to do interesting stuff for malicious users (except phishing maybe, that uses the domain name to look official and makes eg a login form).

That could work if it’s done server side (on the pod) and for all executable code. If we want to only sanitize the “bad part” of the code, I think it would already become tricky to define what is “bad” without missing important security implications.

What are the advantages of running code on the pod, rather than on another server? I guess you need to trust one server less (only the pod, rather than pod + application server) and know which code is executed as it resides on your pod. Is there more I’m missing? I think most if not all use cases can be fulfilled by an external server that has access to your pod (eg a server that logins with a webid and that you give permissions to specific resources).

I’m not sure I understand the relation of SolidOS to this thread. The application includes a static set of features, and yes, iframes are permissive but that should also be not much of a problem if pods sandbox html (currently it is, I’ve opened an issue).

As I use my own pod with apps I know well, I would see a solution that disables html files as a bug rather than a fix and would probably disable that feature fairly quickly :slight_smile: I understand that organisations providing pods or apps would have a very different view on that.

I think best practice would be to have server side sanitation on write and client side sanitation on read. Similar ideas to what was described for the markdown renderer XSS in markdown renderer · Issue #369 · SolidOS/solid-panes · GitHub
Incidentally, best practice for sanitising markdown is to sanitise the html after it is produced.

Dynamic panes in your built-in browser, direct use of web components, local add-ons as per the other thread, hosting apps yourself, not having to run a separate server

Publicly available html pages by themselves do not have access to any of your non public data unless you login. Opening a private html page while not logged in is prevented by requiring a dpop token (cookies aren’t sufficient)
The main attack vector for HTML pages is if the html page is rendered by the browser while you are logged in, which currently only really happens within SolidOS.

I’m not sure how you would exploit most of what you’re referring to without being logged in, which outside of solidos would require tricking somebody into it?

Regarding the features you’ve mentioned:

  • “Dynamic panes in your built-in browser”: (assuming with browser you mean the mashlib data browser) If mashlib has a way to distinguish between custom panes and untrusted html (e.g. by putting the panes in a restricted folder), it could load custom panes while not loading arbitrary html files. Anyway, mashlib loads the html files manually, thus it doesn’t really matter if the server would sandbox them. So I think it’s an issue that needs to be discussed mashlib-specific and is independent of sandboxing html files in pods. (see below)
  • “direct use of web components”: I’m not sure what you mean here. My gut feeling is, that you already need to fetch the data manually anyway because of authorization issues. If that’s the case, adding a sandbox header when serving the html file should not make a difference.
  • “local add-ons as per the other thread”: you could make it pseudo-local by using a redirect html (<meta http-equiv="refresh" content="0; url=http://app.solid?file=..." />, it probably requires specific sandbox settings). Then you still have a url pointing to your pod, but you redirect to an app on a different server.
  • “hosting apps yourself, not having to run a separate server”: yes, these features would be broken, you’d need other servers

So in summary, from these features the main feature loss is that you require external servers for apps. For web components, depends on what exactly you mean. For mashlib, after I realized that it manually fetches the html, I think it’s a mashlib problem mostly independent of html sandboxing by pods.

Yes, the app-app examples above require two apps: one used by the user and one evil app. Currently the main app served on the pod origin is SolidOS, so this is the main target right now. In theory, these examples relate to any app running on the pod origin, no matter if they are predefined by the pod (like SolidOS or eg the Penny CSS recipe) or directly saved as html into the pod. All of these examples take some kind of interesting information originating from a normal app.

The service worker example does not require another app, it can be exploited on its own. Given the preconditions, it adds itself on the client side and becomes a proxy for the files in its scope.

EDIT: I guess cookies are somewhat in between. NSS sets cookies on the login process which are accessible by html files on the pod (Permission escalation because user-uploaded html files can use session cookie · Issue #1719 · nodeSolidServer/node-solid-server · GitHub)

I’m not sure what you mean with this. I guess, in general yes, but for NSS currently cookies are sufficient if you previously logged in.

As you may know, it’s quite possible to serve SolidOS from a non-Solid Server. e.g. github or a localhost. Here’s SolidOS running from github as a webapp. Any thoughts on security considerations related to the issues you bring up?

I think using SolidOS on a different domain would fix some of the issues for this application, but not resolve the discussion about pods serving/sandboxing html files.

For LocalStorage: Apps running on the pod won’t be able to access SolidOS’s local storage

For Cookies: N/A (It does not set cookies, see below)

For IFrames: Partial fix (see below)

Url history: Apps running on the pod can’t change there url to look SolidOS-ish, as it’s now a different domain

Service Workers: Apps running on the pod won’t be able to create a service worker that interfere with SolidOS on a different domain

Regarding the cookies it is a problem of NSS only. In the issue here, NSS sets a session cookie in the login process (nssidp.sid) that will be attached to requests to solidcommunity.net. This will be set regardless from where the login process is started, so it does not matter where SolidOS is served. I previously thought it’s a solution, but I didn’t understand the cookies well enough back then.

Regarding IFrames: It seems to prevent it, if there is no content type != text/html that gets executed in the browser. SolidOS has three ways to create iframes: https://github.com/SolidOS/solid-panes/blob/282a8fe795cb0dd7503ee1e26c59708c041236d0/src/humanReadablePane.js#L103-L121

  1. Files with content type text/html will be rendered directly. If SolidOS runs on github.io, the browser will realize it is cross origin and set the security context accordingly. Thus in this case the iframe accessing its parent is prevented.
  2. Files with text/markdown will be sanitized and rendered as markdown, so it should be safe.
  3. Files with other content types are manually fetched and the src set with the blob. This is dangerous, as (I think, not tested) the blob has the same origin as SolidOS and thus, if the content is executable, it can access its parent (which is the issue we’d like to prevent). I’m not sure if any mime type != text/html executes in the browser. For instance, I’ve tried application/vnd.ms-htmlhelp which sounds like it could be interpreted as html by the browser, but NSS throws a 500 when saving such a file.

These 3 ways of creating iframes also invalidate my statement here. For case 1, if the server sandboxes html it would make a difference.

For handling files, I do think that Solid will need a way to request from a Server a time boxed & authentication bound URL for a resource, such that that URL can be used directly via browser native elements (img, audio, video, etc)

Potentially this could be done through a Link header or a separate dedicated endpoint, but the current “well, we can’t send the Authentication header from elements, so we must have the entire file passed through JavaScript” approach is hugely inefficient.

A dedicated endpoint would probably make most sense, as you could then do batch requests for URLs (so 1 request fetching multiple signed URLs).

Edit: as for serving HTML, I don’t think a Pod should ever serve HTML directly, as the security model in the browser just doesn’t make that safe, yes, some risks can be mitigated, but there’s so much attack surface, it’s just better not to do this. For example, you’ve used iframes to isolate runtime, but what about code that installs a service worker? It’ll have access to all requests to the pod, if installed from the context of the pod’s domain.

If you use CSP, then that blocks javascript, which effectively makes serving HTML for most use cases pointless (as folks want this feature as to not have to worry about application hosting), but JavaScript isn’t the only way to exfiltrate data from a page.