Revision 2138
Added by darkviper about 9 years ago
SecureTokens.php | ||
---|---|---|
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) |
Also available in: Unified diff
! class SecureTokens: added handling of all kind of IPv6 notations