20
20
namespace Phalcon;
21
21
22
22
use Phalcon\DiInterface;
23
+ use Phalcon\Security\Random;
23
24
use Phalcon\Security\Exception;
24
25
use Phalcon\Di\InjectionAwareInterface;
25
26
use Phalcon\Session\AdapterInterface as SessionInterface;
@@ -54,7 +55,11 @@ class Security implements InjectionAwareInterface
54
55
55
56
protected _tokenValueSessionID = " $PHALCON/CSRF$" ;
56
57
57
- protected _csrf;
58
+ protected _token;
59
+
60
+ protected _tokenKey;
61
+
62
+ protected _random;
58
63
59
64
protected _defaultHash;
60
65
@@ -78,6 +83,14 @@ class Security implements InjectionAwareInterface
78
83
79
84
const CRYPT_SHA512 = 9 ;
80
85
86
+ /**
87
+ * Phalcon\Security constructor
88
+ */
89
+ public function __construct ()
90
+ {
91
+ let this -> _random = new Random ();
92
+ }
93
+
81
94
/**
82
95
* Sets the dependency injector
83
96
*/
@@ -96,10 +109,13 @@ class Security implements InjectionAwareInterface
96
109
97
110
/**
98
111
* Sets a number of bytes to be generated by the openssl pseudo random generator
112
+ * @return Phalcon\Security
99
113
*/
100
- public function setRandomBytes (long! randomBytes ) -> void
114
+ public function setRandomBytes (long! randomBytes ) -> <Security>
101
115
{
102
116
let this -> _numberBytes = randomBytes;
117
+
118
+ return this ;
103
119
}
104
120
105
121
/**
@@ -110,34 +126,30 @@ class Security implements InjectionAwareInterface
110
126
return this -> _numberBytes;
111
127
}
112
128
129
+ /**
130
+ * Returns a secure random number generator instance
131
+ * @return Phalcon\Security\Random
132
+ */
133
+ public function getRandom () -> <Random>
134
+ {
135
+ return this -> _random;
136
+ }
137
+
113
138
/**
114
139
* Generate a >22-length pseudo random string to be used as salt for passwords
115
140
*/
116
141
public function getSaltBytes (int numberBytes = 0 ) -> string
117
142
{
118
143
var safeBytes;
119
144
120
- if ! function_exists(" openssl_random_pseudo_bytes" ) {
121
- throw new Exception (" Openssl extension must be loaded" );
122
- }
123
-
124
145
if ! numberBytes {
125
146
let numberBytes = (int ) this -> _numberBytes;
126
147
}
127
148
128
149
loop {
150
+ let safeBytes = this -> _random-> base64Safe(numberBytes);
129
151
130
- /**
131
- * Produce random bytes using openssl
132
- * Filter alpha numeric characters
133
- */
134
- let safeBytes = phalcon_filter_alphanum(base64_encode(openssl_random_pseudo_bytes(numberBytes)));
135
-
136
- if ! safeBytes {
137
- continue ;
138
- }
139
-
140
- if strlen(safeBytes) < numberBytes {
152
+ if ! safeBytes || strlen(safeBytes) < numberBytes {
141
153
continue ;
142
154
}
143
155
@@ -176,6 +188,10 @@ class Security implements InjectionAwareInterface
176
188
let variant = " y" ;
177
189
break ;
178
190
191
+ case self :: CRYPT_MD5 :
192
+ let variant = " 1" ;
193
+ break ;
194
+
179
195
case self :: CRYPT_SHA256 :
180
196
let variant = " 5" ;
181
197
break ;
@@ -193,22 +209,38 @@ class Security implements InjectionAwareInterface
193
209
switch hash {
194
210
195
211
case self :: CRYPT_STD_DES :
212
+ case self :: CRYPT_EXT_DES :
196
213
197
214
/* Standard DES-based hash with a two character salt from the alphabet "./0-9A-Za-z". */
198
215
199
- let saltBytes = this -> getSaltBytes(2 );
216
+ if (hash == self :: CRYPT_EXT_DES ) {
217
+ let saltBytes = " _" .this -> getSaltBytes(8 );
218
+ } else {
219
+ let saltBytes = this -> getSaltBytes(2 );
220
+ }
221
+
200
222
if typeof saltBytes != " string" {
201
223
throw new Exception (" Unable to get random bytes for the salt" );
202
224
}
203
225
204
- break ;
226
+ return crypt(password, saltBytes) ;
205
227
228
+ case self :: CRYPT_MD5 :
229
+ case self :: CRYPT_SHA256 :
206
230
case self :: CRYPT_SHA512 :
207
- let saltBytes = this -> getSaltBytes(8 );
231
+
232
+ /*
233
+ * MD5 hashing with a twelve character salt
234
+ * SHA-256/SHA-512 hash with a sixteen character salt.
235
+ */
236
+
237
+ let saltBytes = this -> getSaltBytes(hash == self :: CRYPT_MD5 ? 12 : 16 );
238
+
208
239
if typeof saltBytes != " string" {
209
240
throw new Exception (" Unable to get random bytes for the salt" );
210
241
}
211
- return crypt(password, " $" . variant . " $" . saltBytes);
242
+
243
+ return crypt(password, " $" . variant . " $" . saltBytes . " $" );
212
244
213
245
case self :: CRYPT_DEFAULT :
214
246
case self :: CRYPT_BLOWFISH :
@@ -239,7 +271,7 @@ class Security implements InjectionAwareInterface
239
271
}
240
272
}
241
273
242
- return crypt(password, " $2" . variant . " $" . sprintf(" %02s" , workFactor) . " $" . saltBytes);
274
+ return crypt(password, " $2" . variant . " $" . sprintf(" %02s" , workFactor) . " $" . saltBytes . " $ " );
243
275
}
244
276
245
277
return " " ;
@@ -286,59 +318,45 @@ class Security implements InjectionAwareInterface
286
318
/**
287
319
* Generates a pseudo random token key to be used as input's name in a CSRF check
288
320
*/
289
- public function getTokenKey (int numberBytes = null ) -> string
321
+ public function getTokenKey () -> string
290
322
{
291
- var safeBytes, dependencyInjector, session;
292
-
293
- if ! numberBytes {
294
- let numberBytes = 12 ;
295
- }
323
+ var dependencyInjector, session;
296
324
297
- if ! function_exists(" openssl_random_pseudo_bytes" ) {
298
- throw new Exception (" Openssl extension must be loaded" );
299
- }
325
+ if null === this -> _tokenKey {
326
+ let dependencyInjector = <DiInterface> this -> _dependencyInjector;
327
+ if typeof dependencyInjector != " object" {
328
+ throw new Exception (" A dependency injection container is required to access the 'session' service" );
329
+ }
300
330
301
- let dependencyInjector = <DiInterface> this -> _dependencyInjector ;
302
- if typeof dependencyInjector != " object " {
303
- throw new Exception ( " A dependency injection container is required to access the 'session' service " );
331
+ let this -> _tokenKey = this -> _random -> base64Safe( this -> _numberBytes) ;
332
+ let session = <SessionInterface> dependencyInjector -> getShared( " session " );
333
+ session -> set ( this -> _tokenKeySessionID, this -> _tokenKey );
304
334
}
305
335
306
- let safeBytes = phalcon_filter_alphanum(base64_encode(openssl_random_pseudo_bytes(numberBytes)));
307
- let session = <SessionInterface> dependencyInjector-> getShared(" session" );
308
- session-> set (this -> _tokenKeySessionID, safeBytes);
309
-
310
- return safeBytes;
336
+ return this -> _tokenKey;
311
337
}
312
338
313
339
/**
314
340
* Generates a pseudo random token value to be used as input's value in a CSRF check
315
341
*/
316
- public function getToken (int numberBytes = null ) -> string
342
+ public function getToken () -> string
317
343
{
318
- var token, dependencyInjector, session;
319
-
320
- if ! numberBytes {
321
- let numberBytes = 12 ;
322
- }
344
+ var dependencyInjector, session;
323
345
324
- if ! function_exists(" openssl_random_pseudo_bytes" ) {
325
- throw new Exception (" Openssl extension must be loaded" );
326
- }
346
+ if null === this -> _token {
347
+ let this -> _token = this -> _random-> base64Safe(this -> _numberBytes);
327
348
328
- let token = openssl_random_pseudo_bytes(numberBytes);
329
- let token = base64_encode(token);
330
- let token = phalcon_filter_alphanum(token);
349
+ let dependencyInjector = <DiInterface> this -> _dependencyInjector;
331
350
332
- let dependencyInjector = <DiInterface> this -> _dependencyInjector;
351
+ if typeof dependencyInjector != " object" {
352
+ throw new Exception (" A dependency injection container is required to access the 'session' service" );
353
+ }
333
354
334
- if typeof dependencyInjector != " object " {
335
- throw new Exception ( " A dependency injection container is required to access the 'session' service " );
355
+ let session = <SessionInterface> dependencyInjector -> getShared( " session " );
356
+ session -> set ( this -> _tokenValueSessionID, this -> _token );
336
357
}
337
358
338
- let session = <SessionInterface> dependencyInjector-> getShared(" session" );
339
- session-> set (this -> _tokenValueSessionID, token);
340
-
341
- return token;
359
+ return this -> _token;
342
360
}
343
361
344
362
/**
@@ -387,8 +405,7 @@ class Security implements InjectionAwareInterface
387
405
* Remove the key and value of the CSRF token in session
388
406
*/
389
407
if returnValue && destroyIfValid {
390
- session-> remove(this -> _tokenKeySessionID);
391
- session-> remove(this -> _tokenValueSessionID);
408
+ this -> destroyToken();
392
409
}
393
410
394
411
return returnValue;
@@ -408,13 +425,14 @@ class Security implements InjectionAwareInterface
408
425
}
409
426
410
427
let session = <SessionInterface> dependencyInjector-> getShared(" session" );
428
+
411
429
return session-> get (this -> _tokenValueSessionID);
412
430
}
413
431
414
432
/**
415
433
* Removes the value of the CSRF token and key from session
416
434
*/
417
- public function destroyToken ()
435
+ public function destroyToken () -> <Security>
418
436
{
419
437
var dependencyInjector, session;
420
438
@@ -428,6 +446,11 @@ class Security implements InjectionAwareInterface
428
446
429
447
session-> remove(this -> _tokenKeySessionID);
430
448
session-> remove(this -> _tokenValueSessionID);
449
+
450
+ let this -> _token = null ;
451
+ let this -> _tokenKey = null ;
452
+
453
+ return this ;
431
454
}
432
455
433
456
/**
@@ -447,10 +470,13 @@ class Security implements InjectionAwareInterface
447
470
448
471
/**
449
472
* Sets the default hash
473
+ * @return Phalcon\Security
450
474
*/
451
475
public function setDefaultHash (int defaultHash )
452
476
{
453
477
let this -> _defaultHash = defaultHash;
478
+
479
+ return this ;
454
480
}
455
481
456
482
/**
0 commit comments