![]() Server : Apache System : Linux server2.corals.io 4.18.0-348.2.1.el8_5.x86_64 #1 SMP Mon Nov 15 09:17:08 EST 2021 x86_64 User : corals ( 1002) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /home/corals/cartforge.co/vendor/laminas/laminas-session/src/Validator/ |
<?php declare(strict_types=1); namespace Laminas\Session\Validator; use Laminas\Session\Container; use Laminas\Validator\AbstractValidator; use function assert; use function explode; use function is_array; use function is_string; use function md5; use function random_bytes; use function sprintf; use function str_replace; use function strtr; /** * @psalm-type OptionsArgument = array{ * name?: non-empty-string, * salt?: non-empty-string, * session?: Container, * timeout?: int, * } */ final class Csrf extends AbstractValidator { /** * Error codes * * @const string */ public const NOT_SAME = 'notSame'; /** * Error messages * * @var array<string, string> */ protected array $messageTemplates = [ self::NOT_SAME => 'The form submitted did not originate from the expected site', ]; /** * Actual hash used. */ private ?string $hash = null; /** * Name of CSRF element (used to create non-colliding hashes) * * @var non-empty-string */ private string $name = 'csrf'; /** * Salt for CSRF token * * @var non-empty-string */ private string $salt = 'salt'; private ?Container $session = null; /** * TTL for CSRF token */ private int|null $timeout = 300; /** @param OptionsArgument $options */ public function __construct(array $options = []) { parent::__construct($options); } /** * Does the provided token match the one generated? * * @param array<string, mixed>|null $context */ public function isValid(mixed $value, array|null $context = null): bool { if (! is_string($value)) { return false; } $this->setValue($value); $tokenId = $this->getTokenIdFromHash($value); $hash = $this->getValidationToken($tokenId); $tokenFromValue = $this->getTokenFromHash($value); $tokenFromHash = $this->getTokenFromHash($hash); if ($tokenFromValue === null || $tokenFromHash === null || ($tokenFromValue !== $tokenFromHash)) { $this->error(self::NOT_SAME); return false; } return true; } /** * Set CSRF name * * @param non-empty-string $name */ public function setName(string $name): void { $this->name = $name; } /** * Get CSRF name * * @return non-empty-string */ public function getName(): string { return $this->name; } /** * Set session container */ public function setSession(Container $session): void { $this->session = $session; if ($this->hash !== null) { $this->initCsrfToken(); } } /** * Get session container * * Instantiate session container if none currently exists */ public function getSession(): Container { if (null === $this->session) { $this->session = new Container($this->getSessionName()); } return $this->session; } /** * Salt for CSRF token * * @param non-empty-string $salt */ public function setSalt(string $salt): void { $this->salt = $salt; } /** * Retrieve salt for CSRF token * * @return non-empty-string */ public function getSalt(): string { return $this->salt; } /** * Retrieve CSRF token * * If no CSRF token currently exists, or should be regenerated, * generates one. */ public function getHash(bool $regenerate = false): string { if ((null === $this->hash) || $regenerate) { $this->generateHash(); } assert($this->hash !== null); return $this->hash; } /** * Get session namespace for CSRF token * * Generates a session namespace based on salt, element name, and class. */ public function getSessionName(): string { return str_replace('\\', '_', self::class) . '_' . $this->getSalt() . '_' . strtr($this->getName(), ['[' => '_', ']' => '']); } /** * Set timeout for CSRF session token */ public function setTimeout(int|null $ttl): void { $this->timeout = $ttl ?? null; } /** * Get CSRF session token timeout */ public function getTimeout(): int|null { return $this->timeout; } /** * Initialize CSRF token in session */ private function initCsrfToken(): void { $session = $this->getSession(); $timeout = $this->getTimeout(); if (null !== $timeout) { $session->setExpirationSeconds($timeout); } $hash = $this->getHash(); $token = $this->getTokenFromHash($hash); $tokenId = $this->getTokenIdFromHash($hash); assert(is_string($tokenId)); $tokenList = $session->tokenList ?? []; assert(is_array($tokenList)); $tokenList[$tokenId] = $token; $session->tokenList = $tokenList; $session->hash = $hash; // @todo remove this, left for BC } /** * Generate CSRF token * * Generates CSRF token and stores both in {@link $hash} and element value. */ private function generateHash(): void { $token = md5($this->getSalt() . random_bytes(32) . $this->getName()); $this->hash = $this->formatHash($token, $this->generateTokenId()); $this->setValue($this->hash); $this->initCsrfToken(); } private function generateTokenId(): string { return md5(random_bytes(32)); } /** * Get validation token * * Retrieve token from session, if it exists. */ private function getValidationToken(string|null $tokenId = null): string|null { $session = $this->getSession(); /** * if no tokenId is passed we revert to the old behaviour * * @todo remove, here for BC */ if ($tokenId === null && isset($session->hash) && is_string($session->hash)) { return $session->hash; } if ($tokenId !== null && isset($session->tokenList[$tokenId]) && is_string($session->tokenList[$tokenId])) { return $this->formatHash($session->tokenList[$tokenId], $tokenId); } return null; } private function formatHash(string $token, string $tokenId): string { return sprintf('%s-%s', $token, $tokenId); } private function getTokenFromHash(?string $hash): ?string { if (null === $hash) { return null; } $data = explode('-', $hash); return $data[0] ?: null; } private function getTokenIdFromHash(string $hash): ?string { $data = explode('-', $hash); if (! isset($data[1])) { return null; } return $data[1]; } }