Revision 2138
Added by darkviper almost 10 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