-
Notifications
You must be signed in to change notification settings - Fork 55
/
Copy pathnocsrf.php
119 lines (103 loc) · 4.23 KB
/
nocsrf.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
<?php
/**
* NoCSRF, an anti CSRF token generation/checking class.
*
* Copyright (c) 2011 Thibaut Despoulain <http://bkcore.com/blog/code/nocsrf-php-class.html>
* Licensed under the MIT license <http://www.opensource.org/licenses/mit-license.php>
*
* @author Thibaut Despoulain <http://bkcore.com>
* @version 1.0
*/
class NoCSRF
{
protected static $doOriginCheck = false;
/**
* Check CSRF tokens match between session and $origin.
* Make sure you generated a token in the form before checking it.
*
* @param String $key The session and $origin key where to find the token.
* @param Mixed $origin The object/associative array to retreive the token data from (usually $_POST).
* @param Boolean $throwException (Facultative) TRUE to throw exception on check fail, FALSE or default to return false.
* @param Integer $timespan (Facultative) Makes the token expire after $timespan seconds. (null = never)
* @param Boolean $multiple (Facultative) Makes the token reusable and not one-time. (Useful for ajax-heavy requests).
*
* @return Boolean Returns FALSE if a CSRF attack is detected, TRUE otherwise.
*/
public static function check( $key, $origin, $throwException=false, $timespan=null, $multiple=false )
{
if ( !isset( $_SESSION[ 'csrf_' . $key ] ) )
if($throwException)
throw new Exception( 'Missing CSRF session token.' );
else
return false;
if ( !isset( $origin[ $key ] ) )
if($throwException)
throw new Exception( 'Missing CSRF form token.' );
else
return false;
// Get valid token from session
$hash = $_SESSION[ 'csrf_' . $key ];
// Free up session token for one-time CSRF token usage.
if(!$multiple)
$_SESSION[ 'csrf_' . $key ] = null;
// Origin checks
if( self::$doOriginCheck && sha1( $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] ) != substr( base64_decode( $hash ), 10, 40 ) )
{
if($throwException)
throw new Exception( 'Form origin does not match token origin.' );
else
return false;
}
// Check if session token matches form token
if ( $origin[ $key ] != $hash )
if($throwException)
throw new Exception( 'Invalid CSRF token.' );
else
return false;
// Check for token expiration
if ( $timespan != null && is_int( $timespan ) && intval( substr( base64_decode( $hash ), 0, 10 ) ) + $timespan < time() )
if($throwException)
throw new Exception( 'CSRF token has expired.' );
else
return false;
return true;
}
/**
* Adds extra useragent and remote_addr checks to CSRF protections.
*/
public static function enableOriginCheck()
{
self::$doOriginCheck = true;
}
/**
* CSRF token generation method. After generating the token, put it inside a hidden form field named $key.
*
* @param String $key The session key where the token will be stored. (Will also be the name of the hidden field name)
* @return String The generated, base64 encoded token.
*/
public static function generate( $key )
{
$extra = self::$doOriginCheck ? sha1( $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] ) : '';
// token generation (basically base64_encode any random complex string, time() is used for token expiration)
$token = base64_encode( time() . $extra . self::randomString( 32 ) );
// store the one-time token in session
$_SESSION[ 'csrf_' . $key ] = $token;
return $token;
}
/**
* Generates a random string of given $length.
*
* @param Integer $length The string length.
* @return String The randomly generated string.
*/
protected static function randomString( $length )
{
$seed = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijqlmnopqrtsuvwxyz0123456789';
$max = strlen( $seed ) - 1;
$string = '';
for ( $i = 0; $i < $length; ++$i )
$string .= $seed{intval( mt_rand( 0.0, $max ) )};
return $string;
}
}
?>