36 |
36 |
* This class is a replacement for the former class SecureForm using the SecureTokensInterface
|
37 |
37 |
*
|
38 |
38 |
* Settings for this class
|
39 |
|
* TYPE KONSTANTE REGISTY-VAR DEFAULTWERT
|
40 |
|
* boolean SEC_TOKEN_FINGERPRINT ($oReg->SecTokenFingerprint) [default=true]
|
41 |
|
* integer SEC_TOKEN_NETMASK4 ($oReg->SecTokenNetmask4) 0-255 [default=24]
|
42 |
|
* integer SEC_TOKEN_NETMASK6 ($oReg->SecTokenNetmask6) 0-128 [default=64]
|
43 |
|
* integer SEC_TOKEN_LIFE_TIME ($oReg->SecTokenLifeTime) 1800 | 2700 | 3600[default] | 7200
|
|
39 |
* TYPE KONSTANTE REGISTY-VAR DEFAULTWERT
|
|
40 |
* boolean SEC_TOKEN_FINGERPRINT ($oReg->SecTokenFingerprint) [default=true]
|
|
41 |
* integer SEC_TOKEN_IPV4_NETMASK ($oReg->SecTokenIpv4Netmask) 0-255 [default=24]
|
|
42 |
* integer SEC_TOKEN_IPV6_PREFIX_LENGTH ($oReg->SecTokenIpv6PrefixLength) 0-128 [default=64]
|
|
43 |
* integer SEC_TOKEN_LIFE_TIME ($oReg->SecTokenLifeTime) 1800 | 2700 | 3600[default] | 7200
|
44 |
44 |
*/
|
45 |
45 |
|
46 |
46 |
class SecureTokens
|
... | ... | |
102 |
102 |
// define the expiretime for this instance
|
103 |
103 |
$this->iExpireTime = time() + $this->iTokenLifeTime;
|
104 |
104 |
// calculate the instance id for this instance
|
105 |
|
$this->sCurrentInstance = $this->encode64(md5($this->iExpireTime.$this->sSalt));
|
|
105 |
$this->sCurrentInstance = $this->encodeHash(md5($this->iExpireTime.$this->sSalt));
|
106 |
106 |
// load array of tokens from session
|
107 |
107 |
$this->loadTokens();
|
108 |
108 |
// at first of all remove expired tokens
|
... | ... | |
144 |
144 |
* @return void
|
145 |
145 |
* @deprecated from WB-2.8.3-SP5
|
146 |
146 |
*/
|
147 |
|
final public function createFTAN() { ; } // do nothing
|
|
147 |
final public function createFTAN()
|
|
148 |
{
|
|
149 |
trigger_error('Deprecated function call: '.__CLASS__.'::'.__METHOD__, E_USER_DEPRECATED);
|
|
150 |
} // do nothing
|
148 |
151 |
|
149 |
152 |
/**
|
150 |
153 |
* Dummy method for backward compatibility
|
151 |
154 |
* @return void
|
152 |
155 |
* @deprecated from WB-2.8.3-SP5
|
153 |
156 |
*/
|
154 |
|
final public function clearIDKEY() { ; } // do nothing
|
|
157 |
final public function clearIDKEY()
|
|
158 |
{
|
|
159 |
trigger_error('Deprecated function call: '.__CLASS__.'::'.__METHOD__, E_USER_DEPRECATED);
|
|
160 |
} // do nothing
|
155 |
161 |
|
156 |
162 |
/**
|
157 |
163 |
* returns the current FTAN
|
... | ... | |
172 |
178 |
}
|
173 |
179 |
$aFtan = $this->aTokens[$this->aLastCreatedFtan];
|
174 |
180 |
$aFtan['name'] = $this->aLastCreatedFtan;
|
175 |
|
$aFtan['value'] = $this->encode64(md5($aFtan['value'].$this->sFingerprint));
|
|
181 |
$aFtan['value'] = $this->encodeHash(md5($aFtan['value'].$this->sFingerprint));
|
176 |
182 |
if (is_string($mMode)) {
|
177 |
183 |
$mMode = strtoupper($mMode);
|
178 |
184 |
} else {
|
... | ... | |
209 |
215 |
// encode the value of all matching tokens
|
210 |
216 |
$aMatchingTokens = array_map(
|
211 |
217 |
function ($aToken) {
|
212 |
|
return $this->encode64(md5($aToken['value'].$this->sFingerprint));
|
|
218 |
return $this->encodeHash(md5($aToken['value'].$this->sFingerprint));
|
213 |
219 |
},
|
214 |
220 |
// extract all matching tokens from $this->aTokens
|
215 |
221 |
array_intersect_key($this->aTokens, $aArguments)
|
... | ... | |
352 |
358 |
{
|
353 |
359 |
if (isset($this->aTokens[$sTokenName])) {
|
354 |
360 |
if ($this->bPreserveAllOtherTokens) {
|
355 |
|
$this->sInstanceToUpdate = $this->aTokens[$sTokenName]['instance'];
|
|
361 |
if ($this->sInstanceToDelete) {
|
|
362 |
$this->sInstanceToUpdate = $this->sInstanceToDelete;
|
|
363 |
$this->sInstanceToDelete = null;
|
|
364 |
} else {
|
|
365 |
$this->sInstanceToUpdate = $this->aTokens[$sTokenName]['instance'];
|
|
366 |
}
|
356 |
367 |
} else {
|
357 |
368 |
$this->sInstanceToDelete = $this->aTokens[$sTokenName]['instance'];
|
358 |
369 |
}
|
... | ... | |
392 |
403 |
*/
|
393 |
404 |
private function buildFingerprint()
|
394 |
405 |
{
|
395 |
|
if (!$this->bUseFingerprint) { return md5('dummy'); }
|
|
406 |
if (!$this->bUseFingerprint) { return md5('this_is_a_dummy_only'); }
|
396 |
407 |
$sClientIp = '127.0.0.1';
|
397 |
408 |
if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)){
|
398 |
409 |
$sClientIp = array_pop(preg_split('/\s*?,\s*?/', $_SERVER['HTTP_X_FORWARDED_FOR'], null, PREG_SPLIT_NO_EMPTY));
|
... | ... | |
401 |
412 |
}else if (array_key_exists('HTTP_CLIENT_IP', $_SERVER)) {
|
402 |
413 |
$sClientIp = $_SERVER['HTTP_CLIENT_IP'];
|
403 |
414 |
}
|
404 |
|
$sFingerprint = __FILE__.PHP_VERSION
|
405 |
|
. isset($_SERVER['SERVER_SIGNATURE']) ? $_SERVER['SERVER_SIGNATURE'] : 'unknown'
|
406 |
|
. isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'AGENT'
|
407 |
|
. $this->calcClientIpHash($sClientIp);
|
408 |
|
return md5($sFingerprint);
|
|
415 |
return
|
|
416 |
__FILE__.PHP_VERSION
|
|
417 |
. isset($_SERVER['SERVER_SIGNATURE']) ? $_SERVER['SERVER_SIGNATURE'] : 'unknown'
|
|
418 |
. isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : 'AGENT'
|
|
419 |
. $this->calcClientIpHash($sClientIp)
|
|
420 |
;
|
409 |
421 |
}
|
410 |
422 |
|
411 |
423 |
/**
|
... | ... | |
417 |
429 |
*/
|
418 |
430 |
private function calcClientIpHash($sRawIp)
|
419 |
431 |
{
|
|
432 |
// clean address from netmask/prefix and port
|
|
433 |
$sPattern = '/^\[?([.:a-f0-9]*)(?:\/[0-1]*)?(?:\]?.*)$/im';
|
|
434 |
$sRawIp = preg_replace($sPattern, '$1', $sRawIp);
|
420 |
435 |
if (strpos($sRawIp, ':') === false) {
|
421 |
|
// calculate IPv4
|
|
436 |
// sanitize IPv4 ---------------------------------------------------------------------- //
|
422 |
437 |
$iIpV4 = ip2long($sRawIp);
|
423 |
|
// calculate netmask
|
|
438 |
// calculate netmask
|
424 |
439 |
$iMask = ($this->iNetmaskLengthV4 < 1)
|
425 |
440 |
? 0
|
426 |
441 |
: bindec(
|
427 |
442 |
str_repeat('1', $this->iNetmaskLengthV4).
|
428 |
443 |
str_repeat('0', 32 - $this->iNetmaskLengthV4)
|
429 |
444 |
);
|
430 |
|
// apply mask
|
|
445 |
// apply mask and reformat to IPv4 string notation.
|
431 |
446 |
$sIp = long2ip($iIpV4 & $iMask);
|
432 |
447 |
} else {
|
433 |
|
// sanitize IPv6
|
|
448 |
// sanitize IPv6 ---------------------------------------------------------------------- //
|
|
449 |
// check if IP includes a IPv4 part and convert this into IPv6 format
|
|
450 |
$sPattern = '/^([:a-f0-9]*?)\:([0-9]{1,3}(?:\.[0-9]{1,3}){3})$/is';
|
|
451 |
if (preg_match($sPattern, $sRawIp, $aMatches)) {
|
|
452 |
$sIpV4Bin = str_pad((string)decbin(ip2long($aMatches[2])), 32, '0', STR_PAD_LEFT) ;
|
|
453 |
$aIpV6Hex = str_split($sIpV4Bin, 16);
|
|
454 |
$sRawIp = $aMatches[1].':'.dechex(bindec($aIpV6Hex[0])).':'.dechex(bindec($aIpV6Hex[1]));
|
|
455 |
}
|
|
456 |
// calculate number of missing words
|
434 |
457 |
$iWords = 8 - count(preg_split('/:/', $sRawIp, null, PREG_SPLIT_NO_EMPTY));
|
|
458 |
// build replacement for '::'
|
435 |
459 |
$sReplacement = $iWords ? implode(':', array_fill(0, $iWords, '0000')) : '';
|
|
460 |
// insert replacements and remove trailing/leading ':'
|
436 |
461 |
$sClientIp = trim(preg_replace('/\:\:/', ':'.$sReplacement.':', $sRawIp), ':');
|
|
462 |
// split all 8 parts from IP into an array
|
437 |
463 |
$aIpV6 = array_map(
|
438 |
464 |
function($sPart) {
|
|
465 |
// expand all parts to 4 hex digits using leading '0'
|
439 |
466 |
return str_pad($sPart, 4, '0', STR_PAD_LEFT);
|
440 |
467 |
},
|
441 |
468 |
preg_split('/:/', $sClientIp)
|
442 |
469 |
);
|
443 |
|
// calculate netmask
|
444 |
|
$iMask = $this->iNetmaskLengthV6;
|
|
470 |
// build binary netmask from iNetmaskLengthV6
|
|
471 |
// and split all 8 parts into an array
|
445 |
472 |
if ($this->iNetmaskLengthV6 < 1) {
|
446 |
473 |
$aMask = array_fill(0, 8, str_repeat('0', 16));
|
447 |
474 |
} else {
|
... | ... | |
451 |
478 |
16
|
452 |
479 |
);
|
453 |
480 |
}
|
454 |
|
// apply mask
|
|
481 |
// iterate all IP parts, apply its mask and reformat to IPv6 string notation.
|
455 |
482 |
array_walk(
|
456 |
483 |
$aIpV6,
|
457 |
484 |
function(&$sWord, $iIndex) use ($aMask) {
|
458 |
485 |
$sWord = sprintf('%04x', hexdec($sWord) & bindec($aMask[$iIndex]));
|
459 |
486 |
}
|
460 |
487 |
);
|
|
488 |
// reformat to IPv6 string notation.
|
461 |
489 |
$sIp = implode(':', $aIpV6);
|
|
490 |
// ------------------------------------------------------------------------------------ //
|
462 |
491 |
}
|
463 |
|
return md5($sIp); // return the hash
|
|
492 |
return md5($sIp); // return the hashed IP string
|
464 |
493 |
}
|
465 |
494 |
|
466 |
495 |
/**
|
... | ... | |
469 |
498 |
* @return string
|
470 |
499 |
* @description reduce the 32char length of a MD5 to 22 chars
|
471 |
500 |
*/
|
472 |
|
private function encode64($sMd5Hash)
|
|
501 |
private function encodeHash($sMd5Hash)
|
473 |
502 |
{
|
474 |
503 |
return rtrim(base64_encode(pack('h*',$sMd5Hash)), '+-= ');
|
475 |
504 |
}
|
... | ... | |
479 |
508 |
*/
|
480 |
509 |
private function getSettings()
|
481 |
510 |
{
|
482 |
|
if (!class_exists('WbAdaptor', false)) {
|
483 |
|
// for WB before 2.8.4
|
484 |
|
$this->bUseFingerprint = defined('SEC_TOKEN_FINGERPRINT')
|
485 |
|
? SEC_TOKEN_FINGERPRINT
|
486 |
|
: $this->bUseFingerprint;
|
487 |
|
$this->iNetmaskLengthV4 = defined('SEC_TOKEN_NETMASK4')
|
488 |
|
? SEC_TOKEN_NETMASK4
|
489 |
|
: $this->iNetmaskLengthV4;
|
490 |
|
$this->iNetmaskLengthV6 = defined('SEC_TOKEN_NETMASK6')
|
491 |
|
? SEC_TOKEN_NETMASK6
|
492 |
|
: $this->iNetmaskLengthV6;
|
493 |
|
$this->iTokenLifeTime = defined('SEC_TOKEN_LIFE_TIME')
|
494 |
|
? SEC_TOKEN_LIFE_TIME
|
495 |
|
: $this->iTokenLifeTime;
|
496 |
|
} else {
|
497 |
|
// for WB from 2.8.4 and up
|
498 |
|
$this->bUseFingerprint = isset($this->oReg->SecTokenFingerprint)
|
499 |
|
? $this->oReg->SecTokenFingerprint
|
500 |
|
: $this->bUseFingerprint;
|
501 |
|
$this->iNetmaskLengthV4 = isset($this->oReg->SecTokenNetmask4)
|
502 |
|
? $this->oReg->SecTokenNetmask4
|
503 |
|
: $this->iNetmaskLengthV4;
|
504 |
|
$this->iNetmaskLengthV6 = isset($this->oReg->SecTokenNetmask6)
|
505 |
|
? $this->oReg->SecTokenNetmask6
|
506 |
|
: $this->iNetmaskLengthV6;
|
507 |
|
$this->iTokenLifeTime = isset($this->oReg->SecTokenLifeTime)
|
508 |
|
? $this->oReg->SecTokenLifeTime
|
509 |
|
: $this->iTokenLifeTime;
|
510 |
|
}
|
|
511 |
$this->bUseFingerprint = isset($this->oReg->SecTokenFingerprint)
|
|
512 |
? $this->oReg->SecTokenFingerprint
|
|
513 |
: $this->bUseFingerprint;
|
|
514 |
$this->iNetmaskLengthV4 = isset($this->oReg->SecTokenNetmask4)
|
|
515 |
? $this->oReg->SecTokenNetmask4
|
|
516 |
: $this->iNetmaskLengthV4;
|
|
517 |
$this->iNetmaskLengthV6 = isset($this->oReg->SecTokenNetmask6)
|
|
518 |
? $this->oReg->SecTokenNetmask6
|
|
519 |
: $this->iNetmaskLengthV6;
|
|
520 |
$this->iTokenLifeTime = isset($this->oReg->SecTokenLifeTime)
|
|
521 |
? $this->oReg->SecTokenLifeTime
|
|
522 |
: $this->iTokenLifeTime;
|
511 |
523 |
$this->iNetmaskLengthV4 = ($this->iNetmaskLengthV4 < 1 || $this->iNetmaskLengthV4 > 32)
|
512 |
524 |
? 0 :$this->iNetmaskLengthV4;
|
513 |
525 |
$this->iNetmaskLengthV6 = ($this->iNetmaskLengthV6 < 1 || $this->iNetmaskLengthV6 > 128)
|
! class SecureTokens: added handling of all kind of IPv6 notations