Web Security : Using crumbs to protect your PHP API (Ajax) call from Cross-site request forgery (CSRF/XSRF) and other vulnerabilities

Standard

Have your API calls ever being used directly by someone without your permission? If yes, read on to find out how can we protect our API’s from such spammers and hackers. Before we go ahead and see a possible solution for this, lets try to list out a few cases, when our API’s can be accessed without our permissions.

Common cases of vulnerable API/Ajax calls

  • Ajax calls having no user authentication: This is the first place where a spammer will try to find out a loop hole. Take this example, suppose I created a group chat plugin for my blog. Since it’s a group chat plugin, I don’t really want the blog viewers to register before they can write a messages. Blog viewer only need to provide their name, email and url (just like wordpress comments). Thereafter, they can write messages which are submitted on the server side using ajax calls. And here is the “problem”. Anyone can pick up the ajax url, write a curl script, post the required parameters and fill up my database with millions of messages.
  • Ajax calls having user authentication: One day I realize my group chat plugin has received more than 1 million messages last night (all spams). Hence I decide to make my blog viewers to register before they can post a message on the group chat plugin, simply because someone is filling up my database by simulating ajax calls through a curl script. Anyone can write a script, since these ajax call do not authenticate the user making the call. But are my ajax calls safe after forcing users to register? NO, a registered user too can simulate these ajax calls and passing authentication by sending the right cookies.

Possible solutions and their flaws
If you look around on web, you will find a bunch of solution to such problems. But then every solution have it’s own problem which forces you not to use them. Listed below are 2 possible solutions to our problem:

  • Using X-Requested-With to protect ajax calls: All famous javascript frameworks like JQuery, YUI, Mootools etc sends an additional header parameter while making an XHR request. These libraries set “X-Requested-With=XMLHttpRequest” header, which can then be used on the server side to detect if the call was made through an ajax call. But a programmer can easily pass these headers using a curl script, making the server believe that the call was made through an XHR request.
  • Using HTTP Referrer: This solution comes in handy for cases when a spammer/hacker try to POST data into your site’s. We can check for the referrer page, before we go ahead and accept the POST data. If the POST data is coming from a page within your site, you go ahead and accept the data, otherwise reject it. But this solution again have it’s shortcomings. HTTP Referrer can be tampered in certain browsers using javascript and they can also be stripped away by some proxies and firewalls.

Using crumbs
Finally the idea is to have crumbs. A unique electronic key which is shared between server and client, and which have a short life time. But how are these useful? Suppose, in my group chat module, upon page load i generate a crumb whose life time is 30 minutes (tunable). Why 30 minutes? Because, I assume my blog viewers to either engage into the group chat module or leave that specific blog post within 30 minutes.

Now whenever a user writes a message, this crumb is passed back to the server side. If user writes a message before 30 minutes, this crumb will be validated and user shout submitted. (30 minutes should take care of 99.99% of the cases). In response, server api sends back the new crumb which should be sent back with the next ajax call.

Now when a spammer try to simulate the ajax request using curl calls, he will not be able to succeed because of the absence of the crumb. But he can capture the crumb from the site and simulate the effect, right? YES he can, but we can take care of this by reducing the life time of the generated crumb.

Generating crumbs using PHP
Here are the two functions, I use to generate and verify crumbs in PHP:

        // user for whom crumb is to be generated
        $uid = "mailsforabhinav@gmail.com";

        // usually $salt = DB_PASSWORD . DB_USER . DB_NAME . DB_HOST . ABSPATH;
        $salt = "abcdefghijklmnopqrstuvwxyz";

        function challenge($data) {
                global $salt;
                return hash_hmac('md5', $data, $salt);
        }

        function issue_crumb($ttl, $action = -1) {
                global $uid;

                // ttl
                $i = ceil(time() / $ttl);

                // log
                echo "Generating crumb at time:".time().", i:".$i.", action:".$action.", uid:".$uid.PHP_EOL;

                // return crumb
                return substr(challenge($i . $action . $uid), -12, 10);
        }

        function verify_crumb($ttl, $crumb, $action = -1) {
                global $uid;

                // ttl
                $i = ceil(time() / $ttl);

                // log
                echo "Verifying crumb:".$crumb." at time:".time().", i:".$i.", action:".$action.", uid:".$uid.PHP_EOL;

                // verify crumb
                if(substr(challenge($i . $action . $uid), -12, 10) == $crumb || substr(challenge(($i - 1) . $action . $uid), -12, 10) == $crumb)
                        return true;
                return false;
        }

I can generate crumbs with a simple call:

$crumb = issue_crumb(300, "group_chat_module");

where $ttl = 300 (required), $action = “group_chat_module” (optional, defaults to -1)

Later on I can verify the crumb using another call:

var_dump(verify_crumb(300, $crumb, "group_chat_module"));

I hope this helps you protecting your API’s. Let me know of better methods to stop such attacks.
Enjoy!

  • what-if-i-do-wrong

    Hey, Dude
    This info is great! I appreciate that.
    By using this crumb, you may stop the CSRF vulnerabilities, but still your DB can be filled up with junk content by a registered spammer ;)
    Reducing the time will affect all other users too, right?
     
     
     

  • http://abhinavsingh.com Abhinav Singh

    Well I have given a hint, but surely you can go ahead and use this as a base to develop something which is perfect. All depends upon your application need and requirements and you can make it as flexible as possible.

    I have used modified form of this trick (which depends upon application requirements) to protect my api’s from XSRF attacks.

  • winda

    Yeah,  another great info, as always….
    but I’m so tired waiting  for your JAXL bosh support release, long long wait :( — sorry for oot –

  • http://aaronsaray.com Aaron Saray

    This is a good introduction.  However, always thinking on the side of exploiting my own scripts, I still haven’t made a crumb/token system I 100% like.
    The first time, I kept only one token – and would invalidate it if a new one was sent.  However, I found some users were using tabs to browse my website – expecting to comment on three tabs in a row.  I would invalidate the first two as the last crumb was the only one valid.
    Next, I did a similar approach to yours where I used a single one with a validation time smaller than what I thought someone would use for spamming.  Turns out, half of my users couldn’t read and comment in that time frame.  They’d get coffee and come back and be invalidated (which brings up a great point:  if you are invalidating people with crumbs, BE NICE IN YOUR MESSAGE.  Don’t accuse them of being a spammer – cuz your legit customer will just be upset and confused).
    The last system I attempted was one that sent back new valid tokens with each request.  Since this is still part of the response, a cleverly written script can just substitute this token in as well for each new request.
    Its not a 100% solution – but it makes their life tougher!

  • http://abhinavsingh.com Abhinav Singh

    Hi Aaron,

    I totally disagree with your point of sending back a new crumb with ajax response. I guess that makes the job of a spammer a lot simpler and easier. He writes a script, if not a success response capture the returned new crumb and start using the new crumb from now on.

    But I agree one should use crumbs with some intelligence on the UI side (for a form driven commenting system). Here are the ideal steps:

    1) If crumb is invalidated show to your users that the session invalidated. 2) Also while sending a fail response from the server also send back user’s comment so that it is not lost and user can resubmit.

    However these is not the ideal steps of an ajax driven commenting system. Because in that case you should either forcibly reload the browser window or ask the users to do the same. Sending back the new crumb as-it-is is not a good solution.

    See how google group do it. Try to comment in a group, but never submit that. Have your food and come back, when you try to submit now, the response will be session expired. Snice google group have an ajax driven commenting system, I then have to reload the browser window to comment. However this can also be acheived by methods other than crumb.

  • http://aaronsaray.com Aaron Saray

    @abhinav
    First, let me clear up the one point: I wouldn’t send back a new crumb if the first one was invalid… cuz that just leaves it open for exactly what you described.  So maybe that part I didn’t communicate correctly.
    Also, I would ask you about step #2… if its an invalidated response, and its an ajax system, why would you need to resend the response back?  Instead, if you’re intending to do it that way, seems like you’d give them a ‘click here’ button to reload (or tell them to reload).  But I would have stored that comment in their session then, and repopulated it when the page was reloaded (if necessary).
     
    Your thoughts?

  • http://abhinavsingh.com Abhinav Singh

    Yeah I agree, 1st point was mis communicated.

    And yes if I send back the response, then I should maintain that on page refresh too, hence better to save in sessions or elsewhere as per convinience.

    But I guess we can still bypass this with a better solution may be, though that will make the processing time a bit longer in case crumb has invalidated. Here we go:

    1) If the crumb is invalidated, well we can atleast check if the incoming crumb was the last on generated. Possibility is, if this is the case, commentor is a human. 2) This section of the code $i = ceil(time() / $ttl);  can be modified for the case, when crumb invalidates.

    This will handle half of your readers not able to read or write comment problem. What do you say?

  • Pingback: Web Security : Using crumbs to protect your PHP API (Ajax) call from Cross-site request forgery (CSRF/XSRF) and other vulnerabilities | Abhi’s Weblog | My Web Development Bookmarks()

  • http://aaronsaray.com Aaron Saray

    I think that would work a lot better – if it matches, just give it a huge ttl – for that one request. and then invalidate it after. :)

  • http://www.zoombits.de/ram RAM

    That was really a good one. I used the concept to develop my website. The script is neatly written without any loop holes. That was the best part of it.

  • sudhir

    Wouldn’t it be better idea to use  session information for generating & validating crumbs? will make bit more secure what it is rt now.
    Anywayz whts $uid ur using ther?

  • http://polprav.blogspot.com/ Polprav

    Hello from Russia!
    Can I quote a post in your blog with the link to you?

  • http://? Tom Sheridan

    I need some help…. being a novis at this how do you get “a valid crumd” assoicated with E-Mail

  • Pingback: Basic Principle of Information Security | 天使不眠()

  • Pingback: How do I check if the request is made via AJAX with PHP? - PHP Solution - Developers Q & A()