TYPO3 v15 dev-main snapshot ()
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
<?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
|
||||
{
|
||||
public 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.
|
||||
*/
|
||||
protected int $lockIPv4PartCount = 4;
|
||||
|
||||
/**
|
||||
* Same setting as lockIP but for IPv6 addresses.
|
||||
*/
|
||||
protected int $lockIPv6PartCount = 8;
|
||||
|
||||
public function __construct(int $lockIPv4PartCount, int $lockIPv6PartCount)
|
||||
{
|
||||
$this->lockIPv4PartCount = $lockIPv4PartCount;
|
||||
$this->lockIPv6PartCount = $lockIPv6PartCount;
|
||||
}
|
||||
|
||||
public function getSessionIpLock(string $ipAddress): string
|
||||
{
|
||||
if ($this->lockIPv4PartCount === 0 && $this->lockIPv6PartCount === 0) {
|
||||
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);
|
||||
|
||||
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 str_contains($ipAddress, ':');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user