Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Bookmarklet security considerations, CSRF, hashed key

My bookmarklet can be called from any website and basically allows the user to insert a row into his collection from afar - if he is logged in.

Now I want to enable CSRF protection for my site and since a bookmarklet is basically non-forged cross site request, I thought about how I could tell it apart from forged ones.
It's not a high-security environment, but I'm also interested in principle.

I thought I had a way to do it figured out, but then realised that it had problems galore.

Original idea

  • generate a random key that is included in the bookmarklet-link. The key's hash is saved in the database. The random key allows access ONLY to the privilege of insertion into this collection and can't be used anywhere else.
  • the bookmarklet loads a longer script from my server, so I could supply a CSRF prevention token this way
  • require the user to be logged in

Problems

  • If I have the bookmarklet key, do I need counter-CSRF tokens?
  • Is there any way that I could protect the bookmarklet key if the user clicks his bookmarklet on a malicious website?
  • I don't want username and password to be stored in the bookmarklet link, because anybody who has access to the computer would get the password as well then, so I decided on the random key.
    • but if I store only the hash, I cannot generate the same bookmarklet link twice, so when the user wants a bookmarklet in another browser/computer he tediously has to import the link from the old one or break support for the old one.
    • but I shouldn't store the cleartext key, because someone who gains access to the database could use this key to insert rows into accounts that don't belong to him.
    • Possible solution I could ask the user to provide his password anytime he creates the bookmarklet and hash the password many times and put that hash in the URL and put the hash of that hash my database. But of course this opens much worse security holes.
      • I could do this with something like "Mother's maiden name" instead
      • I cannot use bcrypt for hashing because of the random salt, right? What hash function would be right? Or would you dismiss the whole idea?
  • If I leave the bookmarklet key out, a malicious website could simply embed the bookmarklet and extract a valid CSRF token from it, right?

Better ideas? Or can't you have CSR without the F?


Edit, specified use case

One thing I simply didn't think about, is the usage of an iframe as suggested by Sripathi Krishnan.

I had not specified my use case, so yes, an iframe is a valid solution to the aforementioned problem.

However, actually my bookmarklet at the moment does do some basic interaction with the website at runtime (meaning the form is there already and the user can change his selection in the website DOM which should change the form). I'm ready to dismiss this functionality for my use case, if it turns out, there's no reasonably-secure way to tell apart forged from non-forged cross-site requests - but I'm still interested on a theoretical level.

like image 308
Ruben Avatar asked Mar 01 '11 17:03

Ruben


1 Answers

You can't make cross-site requests without eliminating the forgery part. But for your use case, I don't think you need cross-site requests.

Lets assume your service allows users to bookmark any pages he wishes. The job of the bookmarklet would be to save {url, title} into the database. At the same time, you want to prevent a malicious website automatically saving urls for a user who is logged in.

Here's what I would do to solve this -

  1. Create a page on your domain that has a standard html form with regular CSRF protection. This page takes in the parameters {url, pagetitle}, but will only save this tuple when the user explicitly clicks the "save" button.
  2. The bookmarklet's job is to load the iframe
  3. Once the iframe loads, its a normal same-origin request

This is more or less what Google reader does as part of its bookmarklet. Here is the code for its bookmarklet - notice it doesn't have any tokens


javascript:
var b=document.body;
var GR________bookmarklet_domain='http://www.google.com';
if(b&&!document.xmlVersion) {
  void(z=document.createElement('script'));
  void(z.src='http://www.google.com/reader/ui/link-bookmarklet.js');
  void(b.appendChild(z));
 }
 else{}

EDIT : You can still support interactions with the iframe approach. The bookmarklet executes in context of the website, so it has access to the DOM. You can interact however you want with the website. When you are ready to save, open up the Iframe. The iframe will be a sort-of confirmation screen with just one save button.

The trick is to delay creation of the iframe. You only create the iframe when the user is ready to save.

like image 159
Sripathi Krishnan Avatar answered Sep 19 '22 13:09

Sripathi Krishnan