Hack Tahoe-LAFS!

back to the Hall Of Fame

solve CSRF attacks by making references unforgeable, not by making them unshareable

(First read the story of how Nathan Wilcox showed us CSRF vulnerabilities in Tahoe-LAFS, the Least-Authority Filesystem and won an "I Hacked Tahoe-LAFS!" t-shirt: Nathan Wilcox and CSRF attacks.)

There is a general principle here which deserves to be more widely appreciated. A CSRF attack looks, under the hood, a lot like sharing. (The difference is that the sharer intends to harm the receiver.) Compare Figure 1 -- CSRF attack and Figure 2 -- sharing.

 bad guy              victim             site
.------. message     .-----. authority .-----.
| >:-} | ----------> | :-| | ========> | ... |
'------'             '-----'           '-----'

Figure 1 -- CSRF attack: the bad guy presents a message (such as a form or a hyperlink or a page with Javascript on it) to the victim who sends a request -- an authority-wielding message -- to the site which has an effect. The victim already had the authority to do that thing, such as to delete her private files, but she didn't realize what the form or hyperlink was going to do when she clicked on it.

 friend               friend             site
.------. message     .-----. authority .-----.
|  :-) | ----------> | :-) | ========> | ... |
'------'             '-----'           '-----'

Figure 2 -- sharing: a friend sends a message (containing something such as a form or a hyperlink or Javascript) to another friend which does something on the site when she clicks on it.

This insight leads us to propose the following aphorism: Solve CSRF attacks by making references unforgeable, not by making them unshareable.

Currently most people who face CSRF attacks assume that the following strategy is the only sensible one:

  1. Separate all resources that can be addressed by URLs into two classes: powerless resources like static public web pages, and powerful resources like the ability to remove a link from a collection of links, or the ability to insert a song into a playlist.
  2. Assign to the powerless resources URLs which users can share with one another.
  3. Assign to the powerful resources URLs which users can't share with one another. (It can be tricky making sure that you don't accidentally prevent the same user from using the resource over time, over multiple logins and logouts, from different computers, and so forth, but it can be done.)

Most people think of CSRF vulnerabilities as being a failure to prevent the sharing of powerful things -- you accidentally made it so that someone else (the attacker) was able to give an HTML page, URL, or Javascript applet to a different user (the victim) which had a powerful effect when the victim used it. And so most people think of fixing CSRF vulnerabilities as making sure that powerful URLs are really, really unshareable, even when malicious attackers attempt to work-around your anti-sharing techniques: Figure 3 -- standard CSRF defense. We think of CSRF vulnerabilities as being failure to prevent forgery -- the attacker was able to generate a reference which would (in the hands of the victim) do something that the attacker himself was not authorized to do.

The strategy of making references to powerful resources unshareable can work, but it is complex, it inconveniences users, and it has an important limitation: it doesn't help you if you want to allow users to share certain powerful resources with one another. The strategy of "make references unshareable" doesn't completely prevent you from making certain resources shareable, but you'll have to implement that sharing separately, not at the web layer (with URLs, HTML, Javascript), but instead as logic inside your web site. This inhibits sharing of resources across sites. Also, when you implement your site-specific sharing logic, you might accidentally reintroduce a CSRF vulnerability.

 bad guy              victim             site
.------. message     .-----. nothing   .-----.
| >:-{ | ----------> | :-| |           | ... |
'------'             '-----'           '-----'

 friend               friend             site
.------. message     .-----. nothing   .-----.
|  :-) | ----------> | :-( |           | ... |
'------'             '-----'           '-----'

Figure 3 -- standard CSRF defense: the web site attempts to prevent the victim from doing anything with a form, a hyperlink, or some Javascript unless that form, hyperlink, or Javascript came directly to that user from the site, without going through any other user first. Obviously, this also prevents sharing (at the web layer).

One problem with this strategy is that the anti-sharing mechanism is an inconvenience to users. It almost always involves logging in with a user name and password, and for the safer variants it might involve logging in repeatedly due to precautions like session expiry. This strategy also completely excludes people who have not registered on your web site from being the recipients of sharing.

Another problem with this CSRF defense is that it prevents web sites from sharing powerful resources with one another, or user-generated content on the same site from sharing powerful resources with content generated by a different user. People have recognized this problem and are currently working on protocols to allow web sites to whitelist which other web-sites should be allowed to share access to which of their powerful resources. This whitelist approach is sure to suffer the same problems that all such whitelists have: it will occasionally let a bad guy share resources, thus enabling a CSRF attack, and it will often prevent good guys from sharing resources, thus preventing valuable new features.

The alternative CSRF defense that we propose is:

  1. Make all references unforgeable.

This completely solves CSRF attacks while also providing a natural, convenient method of sharing: just share the URL. This means that users can share resources with other users even when those other users have never created an account on your web site before. This is what we did in Tahoe-LAFS v0.5.1: Figure 4 -- unforgeable references as CSRF defense.

 bad guy              victim             site
.------. nothing     .-----. nothing   .-----.
| >:-{ |             | :-| |           | ... |
'------'             '-----'           '-----'

 friend               friend             site
.------. authority   .-----. authority .-----.
|  :-) | ==========> | :-) | ========> | ... |
'------'             '-----'           '-----'

Figure 4 -- unforgeable references as CSRF defense: every resource is controlled by an unforgeable reference. The bad guy doesn't have the authority to use the victim's resources, so he can't come up with a message which would use the victim's resources when the victim clicks on his message. The friend does have the authority to his own resource that he wants to share, and he can share that authority with his friend by sending his friend a copy of the reference. (If a message contains information sufficient to access a resource, then we label it "authority".)

One last note: the capability-based strategy of making all references unforgeable does not mean that all references have to be shareable. For resources which you do not want your users to share, unforgeability of references is solely a way to prevent CSRF attack.

Please let us know, by writing to the tahoe-dev mailing list, if you find this strategy to be helpful or if you find it to be problematic.

Thanks to Alberto Berti for HTML formatting help.