forked from phpsec/phpSec
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathphpsec.class.php
More file actions
302 lines (269 loc) · 9.54 KB
/
phpsec.class.php
File metadata and controls
302 lines (269 loc) · 9.54 KB
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
<?php
/**
phpSec - A PHP security library
@author Audun Larsen <[email protected]>
@copyright Copyright (c) Audun Larsen, 2011, 2012
@link https://github.com/phpsec/phpSec
@license http://opensource.org/licenses/mit-license.php The MIT License
@package phpSec
*/
/**
* phpSec core functionality.
*/
class phpsec {
/**
* Config: Charset used for filter methods.
*/
public static $_charset = 'utf-8';
/**
* Config: Database Source Name.
*/
public static $_dsn = null;
/**
* Config: Enable phpSec session handler.
*/
public static $_sessenable = true;
public static $_sessIdRegen = true;
/**
* Config: Enable phpSec secure cookies.
*/
public static $_cookieenable = false;
/**
* User identifier.
*/
public static $uid = null;
/**
* Storage object.
*/
public static $store = null;
/**
* Constant: Hash method to use.
* This is on it's way to be deprecated.
*/
const HASH_TYPE = 'sha256';
/**
* Constant: Version number. Isn't really
* used for something... yet.
*/
const VERSION = '0.3-beta';
/**
* Autoload methos to load required files when needed.
*
* @param string $class
* Class that PHP is looking for.
*
* @return bool
* True on success, false on error.
*/
public static function load($class) {
$basePath = dirname(__FILE__);
$classes['stateless'] = array(
'phpsecCrypt' => 'phpsec.crypt.php',
'phpsecFilter' => 'phpsec.filter.php',
'phpsecPw' => 'phpsec.pw.php',
'phpsecHash' => 'phpsec.hash.php',
'phpsecRand' => 'phpsec.rand.php',
'phpsecYubikey' => 'phpsec.yubikey.php',
);
$classes['stateful'] = array(
'phpsecCache' => 'phpsec.cache.php',
'phpsecOtp' => 'phpsec.otp.php',
'phpsecOtpcard' => 'phpsec.otpcard.php',
'phpsecSession' => 'phpsec.session.php',
'phpsecStore' => 'phpsec.store.php',
'phpsecStoreFilesystem' => 'phpsec.store.filesystem.php',
'phpsecStorePdo' => 'phpsec.store.pdo.php',
'phpsecToken' => 'phpsec.token.php',
);
/* If we don't have a DSN, only get the parts of phpSec that don't need storage. */
if(self::$_dsn === null) {
$classes = $classes['stateless'];
} else {
$classes = array_merge($classes['stateful'], $classes['stateless']);
}
/* Check if we know about the class, and if the file exists. */
if(isset($classes[$class]) && file_exists($basePath.'/phpsec/'.$classes[$class])) {
require_once $basePath.'/phpsec/'.$classes[$class];
return true;
}
return false;
}
/**
* Initialize the library.
* This method actually does several things:
* 1. Registers the phpsec::load() method as autoload function.
* 2. Opens the store. (connect to database etc.)
* 3. Set charset for multibyte functions.
* 4. Enable the phpSec session handler and starts a session (if session handler is enabled).
* 5. Set's or get a UID that is used by phpsecCache.
* 6. Decrypts cookies (if enabled).
*
* If no DSN (storage method) is configured. This method will stop after completing step 1.
*
* @return bool true
* Will always return true.
*/
public static function init() {
/* First of all, register the autoloading function.
* If we have one set from somewhere else, keep it. */
$autoLoadFunctions = spl_autoload_functions();
$autoLoadFunctions[] = 'phpsec::load';
foreach($autoLoadFunctions as $autoLoadFunction) {
spl_autoload_register($autoLoadFunction);
}
/* Autoloader all good to go. If we don't have a storage set
* we can skip the rest of this method. */
if(self::$_dsn === null) {
return true;
}
/* Open store. */
list($storeType, $storeDest) = explode(':', self::$_dsn);
switch($storeType) {
case 'filesystem':
self::$store = new phpsecStoreFilesystem($storeDest);
break;
case 'mysql':
self::$store = new phpsecStorePdo($storeDest);
break;
default:
self::error('Store type('.$storeType.') invalid', E_USER_ERROR);
}
/* Set the charset of the multibyte functions in PHP. */
mb_internal_encoding(self::$_charset);
mb_regex_encoding(self::$_charset);
/* Enable the custom session handler if enabled. */
if(self::$_sessenable === true) {
phpsecSession::$_sessIdRegen = phpsec::$_sessIdRegen;
ini_set('session.save_handler', 'user');
session_set_save_handler(
'phpsecSession::open',
'phpsecSession::close',
'phpsecSession::read',
'phpsecSession::write',
'phpsecSession::destroy',
'phpsecSession::gc'
);
/* Since we set a session cookie on our session handler, disable the built-in cookies. */
ini_set('session.use_cookies', 0);
/* Start a new session. */
session_start();
/* Check the fingerprint to see if it matches, if not clear session data. */
$fingerprint = hash(self::HASH_TYPE, 'phpSec-fingerprint'.$_SERVER['HTTP_USER_AGENT']);
if(!isset($_SESSION['phpSec-fingerprint'])) {
$_SESSION['phpSec-fingerprint'] = $fingerprint;
}
if($fingerprint != $_SESSION['phpSec-fingerprint']) {
$_SESSION = array();
}
}
/* Create a random token for each visitor and store it the users session.
This is for example used to identify owners of cache data. */
if(!isset($_SESSION['phpSec-uid'])) {
self::$uid = self::genUid();
$_SESSION['phpSec-uid'] = self::$uid;
} else {
self::$uid = $_SESSION['phpSec-uid'];
}
/* If the phpSec secure cookie monster is enabled detect encrypted cookies. */
if(self::$_cookieenable === true) {
phpsecCookie::detect();
}
return true;
}
/**
* Provides the library a simple way of reporting errors to the developer using PHPs error
* handler.
*
* @see http://php.net/manual/en/errorfunc.constants.php
*
* @param string $msg
* String containing the error message
*
* @param constant $level
* Error level (optional).
* If none is specified E_USER_WARNING is used.
*/
public static function error($msg, $level = E_USER_WARNING) {
$callee = next(debug_backtrace());
trigger_error($msg.'. (Called from <strong>'.$callee['file'].' line '.$callee['line'].'</strong>)', $level);
}
/**
* Returns a unique identifier in the format spsecified in
* OpenID Authentication 2.0 protocol.
* For example: 2005-05-15T17:11:51ZUNIQUE
* This function is used to generate all unique tokens used by
* phpSec.
* @see http://openid.net/specs/openid-authentication-2_0.html
*
* @param integer $length
* The total length of the uid. Must be above 25.
*/
public static function genUid($length = 50) {
if($length < 25) {
self::error('Length must be longer than 25');
return false;
}
$timeStamp = gmdate('Y-m-d\TH:i:se');
$randLength = $length-strlen($timeStamp);
return $timeStamp.phpsecRand::str($randLength);
}
/**
* Check structure of an array.
* This method checks the structure of an array (only the first layer of it) against
* a defined set of rules.
*
* @param array $array
* Array to check.
*
* @param array $structure
* Expected array structure. Defined for example like this:
* array(
* 'string' => array(
* 'callback' => 'strlen',
* 'params' => array('%val'),
* 'match' => 3,
* ),
* 'not allowed' = false, // Only makes sense with $strict = false
* 'needed' = true,
* ),
*
* @param bool $strict
* If strict is set to false we will allow keys that's not defined in the structure.
*/
public static function arrayCheck($array, $structure, $strict = true) {
$success = true;
/* First compare the size of the two arrays. Return error if strict is enabled. */
if(sizeof($array) != sizeof($structure) && $strict === true) {
self::error('Array does not match defined structure');
return false;
}
/* Loop trough all the defined keys defined in the structure. */
foreach($structure as $key => $callbackArray) {
if(isset($array[$key])) {
/* The key exists in the array we are checking. */
if(is_array($callbackArray) && isset($callbackArray['callback'])) {
/* We have a callback. */
/* Replace %val with the acutal value of the key. */
$callbackArray['params'] = str_replace('%val', $array[$key], $callbackArray['params']);
if(call_user_func_array($callbackArray['callback'], $callbackArray['params']) !== $callbackArray['match']) {
/* Call the *duh* callback. If this returns false throw error, or an axe. */
self::error('Array does not match defined structure. The '.$key.' key did not pass the '.$callbackArray['callback'].' callback');
$success = false;
}
} elseif($callbackArray === false) {
/* We don't have a callback, but we have found a disallowed key. */
self::error('Array does not match defined structure. '.$key.' is not allowed');
$success = false;
}
} else {
/* The key don't exist in the array we are checking. */
if($callbackArray !== false) {
/* As long as this is not a disallowed key, sound the general alarm. */
self::error('Array does not match defined structure. '.$key.' not defined');
$success = false;
}
}
}
return $success;
}
}