Safer web forms with security tokens

A common issue with many web applications is their vulnerability for Cross-Site Request Forgery, or XSRF. It allows a hacker to send a malicious request to a website with an other user’s privileges. Here’s how it works:

  • A hacker creates a page with a form that submits data to example.com.
  • An administrator from example.com is tricked into visiting the page, the form is submitted using JavaScript.
  • The data is handled by example.com as if it came from the administrator (because it did).

This allows a hacker to perform administrative tasks on example.com like editing pages or deleting users.

Solution

Probably the best solution to this problem is using a security token. This is a code (usually a hash) that is send with the form in a hidden field and is only valid for a specific user and a certain period of time.

I recommend using a SHA1 hash created from these components:

  • Information about the user (IP address, user agent, username).
  • Information about the server (hostname, software version).
  • Information about the website (database name, table prefix).
  • Information that expires (user’s session id).

This will result in an unpredictable and seemingly random hash that is still verifiable by the server and difficult to fake.

After the form is submitted the hash is re-created and compared to the token that was send with the form. Only if the hashes match the form is processed, otherwise an error message is displayed. This way even if a hacker manages to find the user’s token the request will fail.

Example (PHP/HTML):

<?php
$authToken = sha1(
  session_id() .
  phpversion() .
  $dbName .
  $_SERVER['REMOTE_ADDR'] .
  $_SERVER['HTTP_USER_AGENT']
  );
 
if ( isset($_POST['submit']) )
{
  if ( isset($_POST['auth_token']) && $_POST['auth_token'] == $authToken )
  {
    // Process form
  }
  else
  {
    // Display error message
  }
}
?>
 
<form id="form" method="post" action="./">
  <fieldset>
    <label for="name">Name:</label>
    <input type="text" name="name" id="name" value=""/>
  </fieldset>
  <fieldset>
    <input type="hidden" name="auth_token" value="<?php echo $authToken ?>"/>
 
    <input type="submit" name="submit" value="Submit"/>
  </fieldset>
</form>

Note that this only really works for POST requests. For this reason GET (regular links) should never be used to pass information that is used for administrative tasks. AJAX requests should also always use POST.

Filed under Programming

Scroll to top

  • Excellent and usefull tip, I'll adapt this one for http://www.phpbbantispam.com ( on which I allready have a set of randomized (hidden and non hidden) fields ).
  • If you're building it on phpBB something similar should already be in there, they use what they call "form tokens" and "confirm boxes" to prevent XSRF.
blog comments powered by Disqus