-
Notifications
You must be signed in to change notification settings - Fork 638
/
IpLocker.php
118 lines (98 loc) · 3.67 KB
/
IpLocker.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
<?php
declare(strict_types=1);
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
namespace TYPO3\CMS\Core\Authentication;
use TYPO3\CMS\Core\Utility\MathUtility;
/**
* Handles the locking of sessions to IP addresses.
*/
class IpLocker
{
const DISABLED_LOCK_VALUE = '[DISABLED]';
/**
* If set to 4, the session will be locked to the user's IP address (all four numbers).
* Reducing this to 1-3 means that only the given number of parts of the IP address is used.
*
* @var int
*/
protected $lockIPv4PartCount = 4;
/**
* Same setting as lockIP but for IPv6 addresses.
*
* @var int
*/
protected $lockIPv6PartCount = 8;
public function __construct(int $lockIPv4PartCount, int $lockIPv6PartCount)
{
$this->lockIPv4PartCount = $lockIPv4PartCount;
$this->lockIPv6PartCount = $lockIPv6PartCount;
}
public function getSessionIpLock(string $ipAddress, bool $enableLocking = true): string
{
if (!$enableLocking) {
return static::DISABLED_LOCK_VALUE;
}
if ($this->isIpv6Address($ipAddress)) {
return $this->getIpLockPartForIpv6Address($ipAddress);
}
return $this->getIpLockPartForIpv4Address($ipAddress);
}
public function validateRemoteAddressAgainstSessionIpLock(string $ipAddress, string $sessionIpLock): bool
{
if ($sessionIpLock === static::DISABLED_LOCK_VALUE) {
return true;
}
$ipToCompare = $this->isIpv6Address($ipAddress)
? $this->getIpLockPartForIpv6Address($ipAddress)
: $this->getIpLockPartForIpv4Address($ipAddress);
return $ipToCompare === $sessionIpLock;
}
protected function getIpLockPart(string $ipAddress, int $numberOfParts, int $maxParts, string $delimiter): string
{
if ($numberOfParts >= $maxParts) {
return $ipAddress;
}
$numberOfParts = MathUtility::forceIntegerInRange($numberOfParts, 1, $maxParts);
$ipParts = explode($delimiter, $ipAddress);
if ($ipParts === false) {
return $ipAddress;
}
for ($a = $maxParts; $a > $numberOfParts; $a--) {
$ipPartValue = $delimiter === '.' ? '0' : str_pad('', strlen($ipParts[$a - 1]), '0');
$ipParts[$a - 1] = $ipPartValue;
}
return implode($delimiter, $ipParts);
}
protected function getIpLockPartForIpv4Address(string $ipAddress): string
{
if ($this->lockIPv4PartCount === 0) {
return static::DISABLED_LOCK_VALUE;
}
return $this->getIpLockPart($ipAddress, $this->lockIPv4PartCount, 4, '.');
}
protected function getIpLockPartForIpv6Address(string $ipAddress): string
{
if ($this->lockIPv6PartCount === 0) {
return static::DISABLED_LOCK_VALUE;
}
// inet_pton also takes care of IPv4-mapped addresses (see https://en.wikipedia.org/wiki/IPv6_address#Representation)
$unpacked = unpack('H*hex', (string)inet_pton($ipAddress)) ?: [];
$expandedAddress = rtrim(chunk_split($unpacked['hex'] ?? '', 4, ':'), ':');
return $this->getIpLockPart($expandedAddress, $this->lockIPv6PartCount, 8, ':');
}
protected function isIpv6Address(string $ipAddress): bool
{
return strpos($ipAddress, ':') !== false;
}
}