Files
alt-mysql/exec/lib/CsrfGuard.php
T
2026-07-04 15:48:19 +02:00

71 lines
1.9 KiB
PHP

<?php
declare(strict_types=1);
final class CsrfGuard
{
private string $secret;
private string $username;
private string $sessionId;
public function __construct(string $secret, string $username, string $sessionId)
{
$this->secret = $secret;
$this->username = $username;
$this->sessionId = $sessionId !== '' ? $sessionId : 'no_session';
}
/**
* @return array{ts:int,token:string}
*/
public function issue(string $intent): array
{
$ts = time();
$sid = $this->sessionId;
return [
'ts' => $ts,
'sid' => $sid,
'token' => $this->buildToken($intent, $ts, $sid),
];
}
public function validate(string $intent, string $timestamp, string $token, int $ttl = 3600, string $sessionHint = ''): bool
{
if (!preg_match('/^[0-9]{1,20}$/', $timestamp)) {
return false;
}
$ts = (int)$timestamp;
$now = time();
if ($ts <= 0 || $ts > ($now + 30) || ($now - $ts) > $ttl) {
return false;
}
$token = trim($token);
$sids = [];
$hint = trim($sessionHint);
if ($hint !== '') {
$sids[] = $hint;
}
$sids[] = $this->sessionId;
$sids[] = 'no_session';
$sids[] = '';
$sids = array_values(array_unique($sids));
foreach ($sids as $sid) {
$expected = $this->buildToken($intent, $ts, $sid);
if (hash_equals($expected, $token)) {
return true;
}
}
return false;
}
private function buildToken(string $intent, int $timestamp, string $sid): string
{
$payload = implode('|', [$this->username, $sid !== '' ? $sid : 'no_session', $intent, (string)$timestamp]);
return hash_hmac('sha256', $payload, $this->secret);
}
}