Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

Andreas Kruger

By: a guest on Oct 31st, 2012  |  syntax: PHP  |  size: 7.64 KB  |  views: 162  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. <?php
  2.  
  3. /**
  4.  * Cross Site Request Forgery protection class.
  5.  *
  6.  * This class implements CSRF. The initiate method needs to be called on top of
  7.  * all pages where CSRF needs to be implemented. The script will save the token
  8.  * in a cookie. Cookies is to prevent an attacker to just spoof the session-
  9.  * identifier. Cookie-data is stored client side.
  10.  *
  11.  * On requerst pages, you can call the static method validate that returns true
  12.  * in case of valid CSRF tokens else false. You need to handle the false-return
  13.  * by your self in your system.
  14.  *
  15.  * Upon each request the token is invalidated.
  16.  *
  17.  * @author Andreas Krüger
  18.  * @copyright (c) 2012, Andreas Krüger
  19.  * @version 1.0
  20.  */
  21. class csrf
  22. {
  23.  
  24.     /**
  25.      * Sets the CSRF token.
  26.      */
  27.     public static function initiate( $renew = false )
  28.     {
  29.         if (!isset($_COOKIE['__csrf_token']) || $renew === true)
  30.         {
  31.             $token = self::generate_token();
  32.             $_COOKIE['__csrf_token'] = $token;
  33.             @setcookie('__csrf_token', $token);
  34.         }
  35.     }
  36.  
  37.     /**
  38.      * Returns a truely random token of 512 characters.
  39.      * Uses openssl_random_pseudo_bytes() if available, else SHA512 if
  40.      * available. If either is not available, then a
  41.      * random string is created using mt_rand and chr(ord()).
  42.      *
  43.      * @return string
  44.      */
  45.     private static function generate_token()
  46.     {
  47.         if (function_exists('openssl_random_pseudo_bytes'))
  48.         {
  49.             $token = bin2hex(openssl_random_pseudo_bytes(256));
  50.         }
  51.         elseif (function_exists('hash_algos') and in_array('sha512', hash_algos()))
  52.         {
  53.             $token = hash("sha512", mt_rand(0, mt_getrandmax()));
  54.         }
  55.         else
  56.         {
  57.             $token = '';
  58.             for ($i = 0; $i < 512; ++$i)
  59.             {
  60.                 $r = mt_rand(0, 35);
  61.                 if ($r < 26)
  62.                     $token .= chr(ord('a') + $r);
  63.                 else
  64.                     $token .= chr(ord('0') + $r - 26);
  65.             }
  66.         }
  67.  
  68.         return $token;
  69.     }
  70.  
  71.     /**
  72.      * Validates a CSRF request. Looks firstly for the CSRF in _POST, and if
  73.      * not found, then in _GET.
  74.      *
  75.      * Will renew CSRF token upon validate request.
  76.      *
  77.      * @return boolean
  78.      */
  79.     public function validate()
  80.     {
  81.         // If the csrf token is not found in cookies, we dont know what to
  82.         // compare. Renew the token.
  83.         if (!isset($_COOKIE['__csrf_token']))
  84.         {
  85.             self::initiate(true); // Renew token
  86.             return false;
  87.         }
  88.         else
  89.             $csrf_token = $_COOKIE['__csrf_token'];
  90.  
  91.         $token = '';
  92.  
  93.         // Request token has to be set and be a string.
  94.         if (isset($_POST['__csrf_token']) && is_string($_POST['__csrf_token']))
  95.             $token = $_POST['__csrf_token'];
  96.  
  97.         // Request token was not found in POST request, lets see if it's in a GET request.
  98.         if (empty($token) && isset($_GET['csrf_token']) && is_string($_GET['__csrf_token']))
  99.             $token = $_GET['__csrf_token'];
  100.  
  101.         // Renew the token upon each request, making sure to invalidate the request everytime.
  102.         self::initiate(true);
  103.  
  104.         // Compare tokens.
  105.         if ($csrf_token == $token)
  106.             return true;
  107.  
  108.         // Tokens wasn't equal
  109.         return false;
  110.     }
  111.  
  112.     /**
  113.      * Returns the current token.
  114.      *
  115.      * @return string
  116.      */
  117.     public static function getToken()
  118.     {
  119.         // If no token is set, renew it to get a fresh one.s
  120.         if (!isset($_COOKIE['__csrf_token']))
  121.             self::initiate(true); // Renew token
  122.  
  123.  
  124. // Return token.
  125.         return $_COOKIE['__csrf_token'];
  126.     }
  127.  
  128. }
  129.  
  130. // Initiate CSRF
  131. csrf::initiate();
  132.  
  133. // Ajax return call:
  134. if (isset($_POST['type']))
  135. {
  136.     echo (csrf::validate() ? 'true' : 'false');
  137.     exit;
  138. }
  139.  
  140. // Form request
  141. $form_result = false;
  142. if (isset($_POST) && count($_POST) > 0)
  143.     $form_result = csrf::validate();
  144. ?>
  145. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
  146. <html xmlns="http://www.w3.org/1999/xhtml">
  147.     <head>
  148.         <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  149.  
  150.         <script src="http://cdn.jquerytools.org/1.2.6/jquery.tools.min.js"></script>
  151.         <script type="text/javascript">
  152.             // Get cookie content
  153.             function getCookie(name) {
  154.                 var cookieValue = null;
  155.                 if (document.cookie && document.cookie != '') {
  156.                     var cookies = document.cookie.split(';');
  157.                     for (var i = 0; i < cookies.length; i++) {
  158.                         var cookie = jQuery.trim(cookies[i]);
  159.                         // Does this cookie string begin with the name we want?
  160.                         if (cookie.substring(0, name.length + 1) == (name + '=')) {
  161.                             cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
  162.                             break;
  163.                         }
  164.                     }
  165.                 }
  166.                 return cookieValue;
  167.             }
  168.  
  169.             // Do a ajax call, read csrf token from cookie.
  170.             function doAjax()
  171.             {
  172.                 if (typeof jQuery != 'undefined')
  173.                 {
  174.                     $.post("csrf.php", { type: "ajax", __csrf_token: getCookie('__csrf_token') },
  175.                     function(data)
  176.                     {
  177.                         $('#result').html('Ajax result: ' + data);
  178.                     });
  179.                 }
  180.             }
  181.  
  182.             function doAjaxWithout()
  183.             {
  184.                 if (typeof jQuery != 'undefined')
  185.                 {
  186.                     $.post("csrf.php", { type: "ajax" },
  187.                     function(data)
  188.                     {
  189.                         $('#result').html('Ajax result: ' + data);
  190.                     });
  191.                 }
  192.             }
  193.  
  194.             if (typeof jQuery != 'undefined')
  195.             {
  196.                 // Append CSRF token upon form submit, getting token from cookie.
  197.                 $(document).ready(function()
  198.                 {
  199.                     $('form').submit(function(e)
  200.                     {
  201.                         if (!$(this).hasClass('notoken'))
  202.                         {
  203.                             // Dont allow form to submit before we added the token
  204.                             e.preventDefault();
  205.  
  206.                             // Add token to the form we're trying to submit
  207.                             $(this).append($("<input>").attr("type", "hidden").attr("name", "__csrf_token").val(getCookie('__csrf_token')));
  208.  
  209.                             // Remove bind and submit form!
  210.                             $(this).unbind('submit').submit();
  211.                         }
  212.                     });
  213.                 });
  214.             }
  215.         </script>
  216.     </head>
  217.     <body>
  218.  
  219. <h3>Form example</h3>
  220. <form action="" method="post">
  221.     <input type="submit" name="send" value="Click me to submit form" />
  222. </form>
  223. <form action="" method="post" class="notoken">
  224.     <input type="submit" name="send" value="Click me to submit form without token" />
  225. </form>
  226. <br>
  227. <?php echo "Form result: " . ($form_result ? 'true' : 'false') . "<br>"; ?>
  228. <h3>Ajax post call example</h3>
  229. <span onclick="doAjax()" style="border: 1px solid #FF0000; padding: 3px;background:#cccccc; cursor: pointer;">Click me with token</span>
  230. <span onclick="doAjaxWithout()" style="border: 1px solid #FF0000; padding: 3px;background:#cccccc; cursor: pointer;">Click me without token</span>
  231. <br>
  232. <br>
  233. <span id="result">Ajax result: false</span>
  234.  
  235. </body>
  236. </html>