mnutt / davros

Personal file storage server
Apache License 2.0
298 stars 35 forks source link

sign previews and show a few more types #35

Closed mnutt closed 2 years ago

mnutt commented 8 years ago

The issue we're trying to solve is that we don't want a rogue file to somehow be displayed within a grain and have the ability to modify its contents. Most of the attack surface is around the preview. These fall into a few categories:

We protect against someone just linking directly to the raw file by using the Content-Disposition method. But this also prevents setting the iframe as the src, since it just prompts the user to download it. In newer browsers, iframes have a sandbox attribute that can prevent the content from running in the context of the original domain. So we need to make sure 1) the browser supports sandboxing, and 2) that the page is actually inside of an iframe.

We can trivially check for the presence of sandboxing support, but unfortunately the browser does not communicate to the server that it is loaded from within an iframe. (X-Frame-Options headers can prevent pages from loading inside iframes, but cannot require pages load inside iframes) So we need some help from the server.

Any time we want to show iframed content, we make an ajax /api/signature request to the server. The server gives us back a token, and we append that token as a ?signature query param. When we make the iframe request, the server looks for the presence of the ?signature query param and validates that it is correct.

The server generates a token by taking the current date as an integer, signing it with hmac using its secret, appending the date and signature together and returning it to the frontend. The server generates a secret when it starts, and every 5 minutes thereafter. It accepts signatures signed with the last two secrets, so a signature is good for between 5 and 10 minutes, depending on when it was generated. Since the iframe is displayed immediately after requesting the token, there hopefully shouldn't be an issue with the signatures expiring too soon.

The server could generate the signature from the path as well, but I couldn't think of a reason why a signature would need to be for a specific path to prevent xss attacks.

This approach also relies on there only ever being one node process running, which is fine for now but could present problems if Sandstorm ever changed to allow grains to run on multiple nodes at once.

kentonv commented 8 years ago

Hmm. Can a sandboxed iframe read its window.location? If so, it could potentially leak its own URL (including the signature) to a third party who can then trick the user into opening it in a non-sandboxed context.

Proposals for solving this:

I lean towards single-use tokens. It seems "more obviously correct" to me. (You could also do both, for defense in depth.)

Another possible problem: A malicious user of the grain could generate the proper token and then trick another user into visiting it. It's somewhat hard to imagine a useful attack that exploits this, but in any case it's easy to prevent this by baking the grain's hostname (which differs for every user) into the HMAC, so you might as well do that. (Or, in the single-use tokens scenario, store the expected hostname in the token map server-side.)

On the other hand I don't see the reason to include the date in the HMAC, since the secret rotates regularly. You might as well HMAC a constant value (or the hostname alone).

if Sandstorm ever changed to allow grains to run on multiple nodes at once.

That would break pretty much everything, so we definitely won't do that. :)

mnutt commented 8 years ago

Thanks for commenting! I was worried I may have forgotten something, and the window.location thing is it. I think single-use tokens will be easy enough to use, and I like the idea of HMACing the hostname. I can use X-Sandstorm-Base-Path, right?

I'm going to hold off on POSTing to the iframe for the time being, because right now the inside of the iframe is just a webdav server and POST has other semantic meaning. It could be done, but would involve a shim proxy.

kentonv commented 8 years ago

You can use X-Sandstorm-Base-Path, but also Host works.

For single-use tokens I think you want the token to be straight-up random, not an HMAC, but you'd store the host with the token server-side and check it when the token is consumed.