0

I'm trying to learn more about website security, but it's confusing. I often feel like I need to be an attacker in order to understand how to defend against one, since I don't know what they are capable of doing. Currently I'm wondering about the best strategy for implementing CSRF tokens. I gather it's best to generate the token at login and include it in form submissions.

But what if I have a members' profile update page that works like this:

  1. Receive member's ID via $_GET
  2. Retrieve member's info from DB
  3. Submit form changes to self
  4. Update DB

I know I should send the token in step 3, but should I also send it via $_GET in step 1, before I query the DB? If so, should I also use it on any members-only page that queries the DB, even if there's no form? Or am I making things unnecessarily complicated? Reading about this stuff does tend to make me a little paranoid.

3
  • No, you only need to send the CSRF token for write actions, so it's only needed for step 3. This is anti-CSRF basics; I'm surprised you didn't find that out when you were reading up about this. It's because you've asked this basic question that people have re-iterated some of the web security basics in their answers.
    – paj28
    Oct 29, 2014 at 15:21
  • @paj28 It may be basic, but the answerers here don't agree with each other. You and Jeroen say I only need the token in step 3, but Paraplastic2 says I need it in step 1 as well. So I'm still confused.
    – gr8dane
    Oct 29, 2014 at 19:51
  • You only need it in step 3. BTW your comment that you need to think like an attacker is quite inspired, most of the good security develops are good pen testers too.
    – paj28
    Oct 29, 2014 at 21:29

3 Answers 3

3

In the context of CSRF and XSS, the attacker is bound by the Same-Origin Policy. XSS can be used to bypass Same-Origin Policy, and perform any action as a user. Where as the goal for CSRF is to perform a specific action, and this is permitted because that action lacks a proof-of-work. If a request lacks a CSRF token, or proof-of-work, it is therefore vulnerable to CSRF.

Additionally, accessing data by the member's ID, which is passed as a GET parameter, is far worse than CSRF and XSS. This is called Insecure Direct Object Reference (IDOR). Simply put, the application permits users to modify data that they don't own. In this context, the attacker isn't bound by the Same-Origin Policy, the attacker is permitted to send arbitrary HTTP requests to your server to get what they want. To protect the member's id, it must be a $_SESSION variable.

This is proposed form is likely vulnerable to both IDOR and CSRF.

I recommend reading all of the OWASP top 10.

6
  • This didn't answer my question. As I said, I would generate the token at login, and the token would be included in the form at step 3. As for accessing the user's data with their ID, that's outside the scope of this question, so I didn't go into details. Also, it was after reading the OWASP article that I was left with this question.
    – gr8dane
    Oct 28, 2014 at 21:33
  • @gr8dane the SOP holds the fundamental answer to your question. Also, your example form is the definition of IDOR, that can't be ignored. You can't pass a user ID as a $_GET, it must be apart of the $_SESSION. The CSRF token is a $_SESSION variable, that is compared to a $_GET or $_POST variable, which creates a CSRF-synchronization token. This is a secret that the attacker cannot know, because of the SOP.
    – rook
    Oct 29, 2014 at 3:31
  • @gr8dane Perhaps you should be asking the question: "How can the same-origin policy protect my web application?"
    – rook
    Oct 29, 2014 at 5:21
  • That SOP document is very long and much of it goes right over my head. Where exactly can I find the "fundamental answer" to the question I actually asked?
    – gr8dane
    Oct 29, 2014 at 19:32
  • Not that it has anything to do with my question, but I have to pass the ID as a $_GET because some members have permission to edit some other members' profile (a parent can edit a child, for example). Of course, I verify their permissions, etc.
    – gr8dane
    Oct 29, 2014 at 19:36
1

Cross Site Request Forgery (also known as CSRF or XSRF attacks) is an attack which allows attackers to execute undesired actions on a web application in which a user currently is authenticated. The attack is possible when the targeted application does not properly validate the origin of the request, and relies solely on the existence of a valid session between the victim’s browser and the application server.

In the most common scenario of a CSRF attack, a logged-on user will access an additional web page provided by the attacker in another tab of the browser. This page will immediately target a sensitive function within the application – which is still open in the other tab – by submitting a specially crafted request. Since the request is submitted from the same browser, the vulnerable application will accept the request and execute the action.

CSRF Attack

Proper CSRF protection is based on preventing attackers from being able to create a granular request for actions in a system. A solution to this type of attack is to implement unique random tokens for sensitive forms. For each form submission, the token should be validated on the server side.

As a side note, these tokens should always be submitted using the POST method. They are usually supplied as a hidden form field.

Here's an example of CSRF implementation for PHP:

  1. Generating a secure CSRF token in PHP
    function generateCSRFKey($key) {
        $token = base64_encode(openssl_random_pseudo_bytes(16));
        $_SESSION['csrf_' . $key] = $token;
        return $token;
    }

You may be tempted to use rand() or uniqid() but they both specifically state that these functions should not be used for generating secure tokens! Also base64_encode() is only used to make sure the value doesn’t break any HTML code.

  1. Checking a submitted token is valid:
function checkCSRFKey($key, $value) {
    if (!isset($_SESSION['csrf_' . $key]))
        return false;
    if (!$value)
        return false;

    if ($_SESSION['csrf_' . $key] !== $value)
        return false;

    unset($_SESSION['csrf_' . $key]); 
    return true;
}

The above code can be used to add a unique token to any form using:

" name="token">
  1. The code to check on the server-side if the supplied token is valid:
$token = $_POST['token'];
if (checkCSRFKey('settings', $token)) {
    // Handle error
}
3
  • As I said, I've been reading up on this stuff, so you didn't tell me anything I hadn't already read. But you didn't answer the question I actually asked, unless I should infer that the statement "tokens should always be submitted using the POST method" means the answer is No.
    – gr8dane
    Oct 28, 2014 at 22:04
  • No csrf exploit?
    – rook
    Oct 29, 2014 at 5:20
  • @gr8dane: Anti-CSRF tokens should be used when submitting requests to the back-end which in return will make changes in the database for example. When querying member data the way you described, it is not required to use Anti-CSRF tokens. Your authorization mechanism should determine, based on the user's session id etc., if the user is allowed to perform this action.
    – Jeroen
    Oct 29, 2014 at 6:21
0

In the context of your description there are a couple concerns. They will be tied into CSRF later. First off, your member’s area should be encrypted. For example, http://www.site.com has public info and is fine to be unencrypted. https://www.site.com/member/* would be where only authenticated users can access their profiles, and do anything else that requires an authenticated user. Anyone browsing to https://www.site.com/member/* without a valid session, would be redirected to the login page. There they would enter their username and password. Upon authenticating the username and password a SSL/TLS (ideally TLS) session would be established. The sessionID would be stored in a cookie with the HTTP and Secure flags set. This ensures protection of each user’s session and more importantly puts the profile update form behind a layer of encryption.

That encryption adds a layer of protection for the form. Even with the form is vulnerable to CSRF, the server can be setup to only allow forms from users with a valid session, which requires an authenticated user. Or another attack to obtain a valid session, either way it’s more work to exploit the CSRF vulnerability. There are a couple different ways to implement CSRF protections. However, the quick version is that it works like a one-time-pad, per request. When a user submits the form, the client calculates the one-time-pad and adds it to the request. The server verifies the session, then the one-time-pad before accepting the request.

4
  • You didn't answer my question. Why would you assume I'm not already doing the things you mention? I didn't go into details about anything outside the scope of the question. I specifically asked about the use of tokens.
    – gr8dane
    Oct 28, 2014 at 21:47
  • @gr8dane Because the encryption acts as a indirect form of protection for the form. Since you didn't mention it any kind of encryption, I did because it was a good fit for you scenario. In your specific question, you would want to sent the token as part of step 1 or between 1 and 2. At minimum, you want to send the token with any sensitive operations like a anything going to a database. Sending it with every request can add some protection but it will also add processing time. So it's probably not worth it. Oct 29, 2014 at 12:34
  • I'm still confused. You say I need the token in step 1, but paj28 and Jeroen say I only need it in step 3.
    – gr8dane
    Oct 29, 2014 at 20:01
  • 3 is the last step you would need it in. However, since it's used as a form of authentication I would validate the CSRF before step 2. Why would you want to retrieve their member info before validating the token? If the CSRF token is bad, the user is up to something and their request shouldn't be touched. I'd want to authenticate before doing anything with their request. Oct 30, 2014 at 12:18

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .