-
-
Notifications
You must be signed in to change notification settings - Fork 31
/
EasyCSRF.php
133 lines (112 loc) · 3.33 KB
/
EasyCSRF.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<?php
namespace EasyCSRF;
use EasyCSRF\Exceptions\InvalidCsrfTokenException;
use EasyCSRF\Interfaces\SessionProvider;
class EasyCSRF
{
/**
* @var SessionProvider
*/
protected $session;
/**
* @var string
*/
protected $session_prefix = 'easycsrf_';
/**
* @param SessionProvider $sessionProvider
*/
public function __construct(SessionProvider $sessionProvider)
{
$this->session = $sessionProvider;
}
/**
* Generate a CSRF token.
*
* @param string $key Key for this token
* @return string
*/
public function generate($key)
{
$key = $this->sanitizeKey($key);
$token = $this->createToken();
$this->session->set($this->session_prefix . $key, $token);
return $token;
}
/**
* Check the CSRF token is valid.
*
* @param string $key Key for this token
* @param string $token The token string (usually found in $_POST)
* @param int $timespan Makes the token expire after $timespan seconds (null = never)
* @param boolean $multiple Makes the token reusable and not one-time (Useful for ajax-heavy requests)
*/
public function check($key, $token, $timespan = null, $multiple = false)
{
$key = $this->sanitizeKey($key);
if (!$token) {
throw new InvalidCsrfTokenException('Invalid CSRF token');
}
$sessionToken = $this->session->get($this->session_prefix . $key);
if (!$sessionToken) {
throw new InvalidCsrfTokenException('Invalid CSRF session token');
}
if (!$multiple) {
$this->session->set($this->session_prefix . $key, null);
}
if ($this->referralHash() !== substr(base64_decode($sessionToken), 10, 40)) {
throw new InvalidCsrfTokenException('Invalid CSRF token');
}
if ($token != $sessionToken) {
throw new InvalidCsrfTokenException('Invalid CSRF token');
}
// Check for token expiration
if (is_int($timespan) && (intval(substr(base64_decode($sessionToken), 0, 10)) + $timespan) < time()) {
throw new InvalidCsrfTokenException('CSRF token has expired');
}
}
/**
* Sanitize the session key.
*
* @param string $key
* @return string
*/
protected function sanitizeKey($key)
{
return preg_replace('/[^a-zA-Z0-9]+/', '', $key);
}
/**
* Create a new token.
*
* @return string
*/
protected function createToken()
{
// time() is used for token expiration
return base64_encode(time() . $this->referralHash() . $this->randomString(32));
}
/**
* Return a unique referral hash.
*
* @return string
*/
protected function referralHash()
{
return sha1($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']);
}
/**
* Generate a random string.
*
* @param int $length
* @return string
*/
protected 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;
}
}