/
Token.php
150 lines (138 loc) · 4.71 KB
/
Token.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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
<?php
namespace Concrete\Core\Validation\CSRF;
use Concrete\Core\Logging\Channels;
use Concrete\Core\Support\Facade\Application;
use Concrete\Core\User\User;
use Concrete\Core\Http\Request;
use Concrete\Core\Http\Service\Ajax;
class Token
{
/**
* Duration (in seconds) of a token.
*
* @var int
*/
const VALID_HASH_TIME_THRESHOLD = 86400; // 24 hours
/**
* The default name of the token parameters.
*
* @var string
*/
const DEFAULT_TOKEN_NAME = 'ccm_token';
/**
* Get the error message to be shown to the users when a token is not valid.
*
* @return string
*/
public function getErrorMessage()
{
$app = Application::getFacadeApplication();
$request = $app->make(Request::class);
$ajax = $app->make(Ajax::class);
if ($ajax->isAjaxRequest($request)) {
return t("Invalid token. Please reload the page and retry.");
} else {
return t("Invalid form token. Please reload this form and submit again.");
}
}
/**
* Create the HTML code of a token.
*
* @param string $action An optional identifier of the token
* @param bool $return Set to true to return the generated code, false to print it out
*
* @return string|void
*/
public function output($action = '', $return = false)
{
$hash = $this->generate($action);
$token = '<input type="hidden" name="' . static::DEFAULT_TOKEN_NAME . '" value="' . $hash . '" />';
if (!$return) {
echo $token;
} else {
return $token;
}
}
/**
* Generates a token for a given action. This is a token in the form of time:hash, where hash is md5(time:userID:action:pepper).
*
* @param string $action An optional identifier of the token
* @param int $time The UNIX timestamp to be used to determine the token expiration
*
* @return string
*/
public function generate($action = '', $time = null)
{
$app = Application::getFacadeApplication();
$u = $app->make(User::class);
$uID = $u->getUserID();
if (!$uID) {
$uID = 0;
}
if (!$time) {
$time = time();
}
$app = Application::getFacadeApplication();
$config = $app->make('config/database');
$hash = $time . ':' . md5($time . ':' . $uID . ':' . $action . ':' . $config->get('concrete.security.token.validation'));
return $hash;
}
/**
* Generate a token and return it as a query string variable (eg 'ccm_token=...').
*
* @param string $action
*
* @return string
*/
public function getParameter($action = '')
{
$hash = $this->generate($action);
return static::DEFAULT_TOKEN_NAME . '=' . $hash;
}
/**
* Validate a token against a given action.
*
* Basically, we check the passed hash to see if:
* a. the hash is valid. That means it computes in the time:action:pepper format
* b. the time included next to the hash is within the threshold.
*
* @param string $action The action that should be associated to the token
* @param string $token The token to be validated (if empty we'll retrieve it from the current request)
*
* @return bool
*/
public function validate($action = '', $token = null)
{
$app = Application::getFacadeApplication();
if ($token == null) {
$request = $app->make(Request::class);
$token = $request->request->get(static::DEFAULT_TOKEN_NAME);
if ($token === null) {
$token = $request->query->get(static::DEFAULT_TOKEN_NAME);
}
}
if (is_string($token)) {
$parts = explode(':', $token);
if ($parts[0] && isset($parts[1])) {
$time = $parts[0];
$hash = $parts[1];
$compHash = $this->generate($action, $time);
$now = time();
if (substr($compHash, strpos($compHash, ':') + 1) == $hash) {
$diff = $now - $time;
//hash is only valid if $diff is less than VALID_HASH_TIME_RECORD
return $diff <= static::VALID_HASH_TIME_THRESHOLD;
} else {
$logger = $app->make('log/factory')->createLogger(Channels::CHANNEL_SECURITY);
$u = $app->make(User::class);
$logger->debug(t('Validation token did not match'), [
'uID' => $u->getUserID(),
'action' => $action,
'time' => $time,
]);
}
}
}
return false;
}
}