Project

General

Profile

« Previous | Next » 

Revision 2138

Added by darkviper about 9 years ago

! class SecureTokens: added handling of all kind of IPv6 notations

View differences:

branches/2.8.x/CHANGELOG
10 10
# = Bugfix
11 11
! = Update/Change
12 12
===============================================================================
13
05 Nov -2015 Build 2138 Manuela v.d.Decken(DarkViper)
14
! class SecureTokens: added handling of all kind of IPv6 notations
13 15
30 Oct -2015 Build 2137 Manuela v.d.Decken(DarkViper)
14 16
! class SecureTokens: added easy handling of service requests on checkFTAN()/checkIDKEY()
15 17
22 Oct -2015 Build 2136 Manuela v.d.Decken(DarkViper)
branches/2.8.x/wb/admin/interface/version.php
51 51

  
52 52
// check if defined to avoid errors during installation (redirect to admin panel fails if PHP error/warnings are enabled)
53 53
if(!defined('VERSION')) define('VERSION', '2.8.4');
54
if(!defined('REVISION')) define('REVISION', '2137');
54
if(!defined('REVISION')) define('REVISION', '2138');
55 55
if(!defined('SP')) define('SP', '');
branches/2.8.x/wb/framework/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