| 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