#1
  1. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Sep 2006
    Posts
    1,916
    Rep Power
    533

    Preventing Cross-Site Request Forgeries (CSRF)


    I am having a difficult time understanding CSRF and how to prevent it.

    My understanding is that CSRF exploits script which just confirms whether a given $_SESSION variable is set to determine whether the user is logged in. If so, then it is possible to trick them and/or their browser to send some maliciousness request. Seem accurate?

    To prevent it, a random key must be located on the server (in a session, right?) and somewhere on the client, and then each time the server is accessed, make sure they match. Again, seem accurate?

    It is the "somewhere on the client" that I even more question.

    If a traditional form is used, then put the key in a hidden input.

    If AJAX GET or POST is used, keep the key as a hidden input, but use JavaScript to grab it and include it with the data being sent to the server.

    If there are any links on your page which should only be able to be viewed by logged on users, then include the key in the URL of the link.

    Third time, seem accurate? I am thinking I might not have to worry about the AJAX GET calls or the links since there might not be anyone to view the data, but am not certain.

    Thanks
  2. #2
  3. Did you steal it?
    Devshed Supreme Being (6500+ posts)

    Join Date
    Mar 2007
    Location
    Washington, USA
    Posts
    13,961
    Rep Power
    9397
    CSRFs come in two flavors:
    1. Trick the user into submitting a form containing malicious data.
    2. Trick the user into clicking a link which "submits" form data, but the data is allowed to be in the URL instead of just a form. As in the code uses $_REQUEST rather than explicitly $_POST.

    Both of those are defeated by generating a random identifier in the session, hiding it in the form, and comparing the two. The malicious link/form has no idea what that identifier is because it changes every time, and any attempt will present the user with some sort of error message.

    If you use AJAX then try to use a mechanism which serializes the entire form. Otherwise yes, the token has to be included and then regenerated and returned in the response.
  4. #3
  5. --
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,957
    Rep Power
    1045
    Hi,

    Originally Posted by NotionCommotion
    My understanding is that CSRF exploits script which just confirms whether a given $_SESSION variable is set to determine whether the user is logged in. If so, then it is possible to trick them and/or their browser to send some maliciousness request. Seem accurate?
    Yes. Since the victim's browser automatically sends the session cookie, the request will pass the login check. If there's no other protection, the attacker can do anything the user can do.



    Originally Posted by NotionCommotion
    To prevent it, a random key must be located on the server (in a session, right?) and somewhere on the client, and then each time the server is accessed, make sure they match. Again, seem accurate?
    Nope, that's not true. You either store the key on the server or on the client. Each one is possible, but it makes no sense to do it at the same time.

    The key has to be associated with the user, and it must neither be readable nor writable by others. This can be achieved by putting it into the session or by storing it in a cookie (or any other client storage). When generating a form, you fetch this key and include it in a hidden field. When receiving a form submission, you check whether the key in the form matches the one in the session/cookie.

    Since an external attacker can neither read nor write a cookie for your website, he/she cannot forge a valid request (unless there are other security holes, especially XSS vulnerabilities).






    Originally Posted by NotionCommotion
    If AJAX GET or POST is used, keep the key as a hidden input, but use JavaScript to grab it and include it with the data being sent to the server.
    Not sure what you mean by "hidden input" in the context of AJAX. What does AJAX have to do with HTML forms?

    When doing an AJAX request, you simply fetch the key from the cookie/session and send it together with the other data.



    Originally Posted by NotionCommotion
    If there are any links on your page which should only be able to be viewed by logged on users, then include the key in the URL of the link.

    Third time, seem accurate? I am thinking I might not have to worry about the AJAX GET calls or the links since there might not be anyone to view the data, but am not certain.
    A CSRF attack doesn't allow the attacker to actually view the response. He/she can only trigger an action.

    And since GET requests should never have side-effects (i. e. cause some action), CSRF protection only applies to POST requests, either through a form or an AJAX request.
    The 6 worst sins of security ē How to (properly) access a MySQL database with PHP

    Why canít I use certain words like "drop" as part of my Security Question answers?
    There are certain words used by hackers to try to gain access to systems and manipulate data; therefore, the following words are restricted: "select," "delete," "update," "insert," "drop" and "null".
  6. #4
  7. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Sep 2006
    Posts
    1,916
    Rep Power
    533
    Thanks requinix,

    I typically serialize inputs in forms when using Ajax POST, and will continue to do so as you advise. If not possible, you see any problem with the following?
    Code:
    $.post("test.php", { secretCode:$('#secretCode').val() } );
    Also, is CSRFs a risk for Ajax GET requests or for simple links on my page? The only time I do something permanent with GET requests is sometimes update a log that the given user accessed the page.
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Sep 2006
    Posts
    1,916
    Rep Power
    533
    Thanks Jacques1. I very much appreciated your post on the 7 deadly sins!

    You either store the key on the server or on the client.
    I meant on the server (via a session), and also include it in a hidden field within the the form which is generated by the session and sent to the client. I didn't think of using a cookie instead of a session, and it seems like this might not be desired because the bad guy could send both (but I really don't understand this well enough to know).

    Not sure what you mean by "hidden input" in the context of AJAX. What does AJAX have to do with HTML forms?

    When doing an AJAX request, you simply fetch the key from the cookie/session and send it together with the other data.
    I see AJAX is client side javaScript which uses XMLHttpRequest(). How can the client access data in the (PHP) session? The client can access stuff on the page, and if I put <input type="hidden" value="12321312" name="secretCode"> on the page, then I can grab it and add it to the data sent via the Ajax call. Am I missing something?
  10. #6
  11. --
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,957
    Rep Power
    1045
    Originally Posted by NotionCommotion
    I meant on the server (via a session), and also include it in a hidden field within the the form which is generated by the session and sent to the client.
    Yes, that's probably the standard way of doing it.



    Originally Posted by NotionCommotion
    I didn't think of using a cookie instead of a session, and it seems like this might not be desired because the bad guy could send both (but I really don't understand this well enough to know).
    The cookie would indeed get sent with every request, but that's also true for the session cookie. However, both is perfectly fine. The attacker has to actually know the key to be able to forge a request. It doesn't help him/her at all that the victim carries the key.

    So as long as the key is neither readable nor writable for others, anything works.

    By the way: Regenering the key for every form isn't really a good idea. It causes massive usability problems in tabbed browsing (which isn't too uncommon nowadays), because every new tab will invalidate all previous ones. Unless the user keeps track of when he opened which tab, he/she will run into invalid keys all the time. Also it doesn't make a lot of sense to change the key more often than the session ID (which is much more critical). Just use one key per session.



    Originally Posted by NotionCommotion
    I see AJAX is client side javaScript which uses XMLHttpRequest(). How can the client access data in the (PHP) session? The client can access stuff on the page, and if I put <input type="hidden" value="12321312" name="secretCode"> on the page, then I can grab it and add it to the data sent via the Ajax call. Am I missing something?
    Well, personally, I'd put the token directly in the JavaScript code. But fetching it from the form works just as well.
    The 6 worst sins of security ē How to (properly) access a MySQL database with PHP

    Why canít I use certain words like "drop" as part of my Security Question answers?
    There are certain words used by hackers to try to gain access to systems and manipulate data; therefore, the following words are restricted: "select," "delete," "update," "insert," "drop" and "null".
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Sep 2006
    Posts
    1,916
    Rep Power
    533
    Well, personally, I'd put the token directly in the JavaScript code. But fetching it from the form works just as well.
    How? Do you use PHP to create a little bit of JavaScript in your page?
  14. #8
  15. --
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,957
    Rep Power
    1045
    Originally Posted by NotionCommotion
    How? Do you use PHP to create a little bit of JavaScript in your page?
    Yes. But if you do it, make sure that nobody can inject code. Encode every value with json_encode() before inserting it.
    The 6 worst sins of security ē How to (properly) access a MySQL database with PHP

    Why canít I use certain words like "drop" as part of my Security Question answers?
    There are certain words used by hackers to try to gain access to systems and manipulate data; therefore, the following words are restricted: "select," "delete," "update," "insert," "drop" and "null".
  16. #9
  17. No Profile Picture
    Contributing User
    Devshed Intermediate (1500 - 1999 posts)

    Join Date
    Sep 2006
    Posts
    1,916
    Rep Power
    533
    Yes. But if you do it, make sure that nobody can inject code. Encode every value with json_encode() before inserting it.
    Thank you Jacques1, I don't know if I understand. Wouldn't I do something just like the following? Where does json_encode() come into play?
    PHP Code:
    echo('<script>
    $.post("test.php", { secretCode:'
    .$secretCode.' } );
    </script>'
    ); 
    PS. How did you change the font for "json_encode()" in your previous post?
  18. #10
  19. --
    Devshed Expert (3500 - 3999 posts)

    Join Date
    Jul 2012
    Posts
    3,957
    Rep Power
    1045
    Putting arbitrary strings into JavaScript is just a dangerous as putting arbitrary strings into a query. Both comes with the risk of attackers injecting code.

    I mean, imagine something like this:
    PHP Code:
    <?php // don't use ?>
    <script>
    var foo = <?php echo $_GET['bar'?>;
    </script>
    This would allow anybody to inject any JavaScript code via the URL.

    To prevent situations like this, it's a good idea to use json_decode() for generating JavaScript literals (numbers, strings, arrays, objects etc.). This way you can be sure you only insert a single value and not a complete piece of JavaScript code:

    PHP Code:
    echo('<script> 
    $.post("test.php", { secretCode:'
    .json_encode($secretCode).' } ); 
    </script>'
    ); 


    Originally Posted by NotionCommotion
    PS. How did you change the font for "json_encode()" in your previous post?
    With the [ Font ] selection above the message textarea.
    The 6 worst sins of security ē How to (properly) access a MySQL database with PHP

    Why canít I use certain words like "drop" as part of my Security Question answers?
    There are certain words used by hackers to try to gain access to systems and manipulate data; therefore, the following words are restricted: "select," "delete," "update," "insert," "drop" and "null".

IMN logo majestic logo threadwatch logo seochat tools logo