Tokens in PHP- FAO Rob

Bes

Bes

Soldato
Joined
18 Oct 2002
Posts
7,318
Location
Melbourne
Rob's security guide has this code for tokens:

Code:
<?php

session_start();

if( !empty($_POST['post_id'] ) {
    if( !user->is_a_moderator )
        die;
    if( empty($_POST['token']) || $_POST['token'] != $_SESSION['token'] )
        die;

    // All fine: delete the post.
    delete_post( intval($_POST['post_id']) );

    // Unset the token, so that it cannot be used again.
    unset($_SESSION['token']);
}

$token = md5(uniqid(rand(), true));
$_SESSION['token'] = $token;

?>

<form method="post">

<p>Post ID to delete:</p>
<p><input type="text" name="post_id" /></p>

<input type="hidden" name="token" value="<?php echo $token; ?>" />

</form>

What stops a 'baddie' from simply viewing the HTML code before submitting their 'fake' form and creating a form field based on the contents of the hidden token field?

Thanks
 
The key thing there is use of session data. The sequence is thus:

  1. Server generates token and stores it in session data, sends same token to client.
  2. Client sends same token back to server to authenticate.
  3. Server finds the session associated with that client and compares the two tokens.
Baddie would have to have both the same session and the same token to pass validation.

In fact, the key point here is how robust PHP's session support is. I don't know because I haven't looked, but in any event it does make the baddie's job rather more challenging because they must achieve a state where both client and server agree.
 
Ok but that is easy- all I need to do is load the form, get hold of the token, then plug that into my own form and submit it- because the session was generated by PHP on the server and I am sending the same key (which I can see by viewing the page source) that is held in the session, I can't see that this provides much security at all if one is determined.
 
Ok but that is easy- all I need to do is load the form, get hold of the token, then plug that into my own form and submit it- because the session was generated by PHP on the server and I am sending the same key (which I can see by viewing the page source) that is held in the session, I can't see that this provides much security at all if one is determined.

The token is designed to defend against a 'Cross-Site Request Forgery' (CSRF/XSRF) attack. For example I could construct a URL that deletes a specific thread on these forums (For example .../delete.php?thread=27433). If I was somehow able to trick someone with the correct privileges to visit the link then the thread would be deleted, this would be a CSRF attack.

In order to prevent this a unique token is stored in the user's session and then checked whenever a form is submitted (by it's inclusion as a hidden input field). This means that a malicious link as described above wouldn't work as there is no token present. This assumes that the form is using GET, the link could be a simple hyperlink or a form submission.

Even if the attacker was to create a form that goes to the malicious URL they would have no way of knowing what the victim's token was. Yes the attacker can generate a token for themselves along with a session but it's not what it's designed to defend against. It's designed to prevent users getting tricked into clicking malicious links.
 
Ok but that is easy- all I need to do is load the form, get hold of the token, then plug that into my own form and submit it- because the session was generated by PHP on the server and I am sending the same key (which I can see by viewing the page source) that is held in the session, I can't see that this provides much security at all if one is determined.

That wouldn't work as the token is stored in the users session data, thus there is no way (that I know of anyway) for an attacker to gain this token.

EDIT: was too slow :(
 
Ok but that is easy- all I need to do is load the form, get hold of the token, then plug that into my own form and submit it- because the session was generated by PHP on the server and I am sending the same key (which I can see by viewing the page source) that is held in the session, I can't see that this provides much security at all if one is determined.

The point is that you can't do it with the privileges of another user. If you already have permission to perform the stated action, then there's not any need to prevent a CSRF attack because you can just do the action the normal way.

The point is to prevent you from tricking me into visiting the URL and performing an action that I have permission to perform but that you don't.
 
Ok so what is to stop a user making their own form and substituting a radio button for a text field and sending "Badcode" to my form when the options I have allowed are "blue" and "red"? Is the only way to sanitize everything and hope for the best? or can I actively stop submissions from forms that are not on my site?
 
Ok so what is to stop a user making their own form and substituting a radio button for a text field and sending "Badcode" to my form when the options I have allowed are "blue" and "red"? Is the only way to sanitize everything and hope for the best? or can I actively stop submissions from forms that are not on my site?

Of course you must sanitise/check everything that's posted back to you. My personal framework has a two (well, three) pronged approach: A validation system that performs checks on the client side and the server side from a single validation definition (built into the controller and view system) and a filter class that lets you set various rules (field must be an integer, run an anti XSS filter etc.). Both systems are extensible (two Interfaces that you implement so custom validators and filters can be knocked up quickly) and have some generic ones on there (regex filtering/validation, data type checking etc.).
 
can I actively stop submissions from forms that are not on my site?

Not easily, if at all.

Ok so what is to stop a user making their own form and substituting a radio button for a text field and sending "Badcode" to my form when the options I have allowed are "blue" and "red"?

Nothing, they could easily do that.

Is the only way to sanitize everything and hope for the best?

Yes, you should never rely on client side validation, you must always assume that the client is malicious. That's not to say don't do client side validation, it can be useful to provide a responsive user interface. The important thing is that the server side code should be sanitizing everything that comes in from the form, you don't need to go as far as checking that the value is only 'red' or 'blue', you can make the input safe and then check that the value is meaningful without worrying that the input is malicious.

The part of Rob M's guide you were reading was regarding CSRF attacks which is not what you are talking about here, this is simple input validation.
 
Last edited:
Ok so what is to stop a user making their own form and substituting a radio button for a text field and sending "Badcode" to my form when the options I have allowed are "blue" and "red"? Is the only way to sanitize everything and hope for the best?

This isn't what tokens are designed to prevent; the point is to prevent a user with higher privileges than the attacker from being tricked into making a request to your site that has undesired effects (e.g. modifying a user record, for example). This is easier than you might expect: the attacker could put a URL to your site that has such effects in image tags in his signature on a forum, for example. In this case, the privileged user's browser will attempt to load the image, and in doing so will make the request to your site.

Obviously, this particular example can be prevented by requiring that all such actions use POST rather than GET, but the most solid way of doing it is with tokens.

or can I actively stop submissions from forms that are not on my site?

No, but this shouldn't matter if you're sanitizing input properly.
 
Last edited:
I always treat client data as malicious because it's amazing what some people are capable of typing! Or naming their child:

exploits_of_a_mom.png
 
Back
Top Bottom