44 lines
1.1 KiB
PHP
44 lines
1.1 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
namespace NeonBlog;
|
|
|
|
final class Captcha
|
|
{
|
|
/** @return array{token: string, question: string} */
|
|
public static function challenge(): array
|
|
{
|
|
if (session_status() !== PHP_SESSION_ACTIVE) {
|
|
return ['token' => '', 'question' => ''];
|
|
}
|
|
|
|
$a = random_int(2, 12);
|
|
$b = random_int(2, 12);
|
|
$token = bin2hex(random_bytes(12));
|
|
$_SESSION['captcha'][$token] = [
|
|
'answer' => (string) ($a + $b),
|
|
'expires' => time() + 900,
|
|
];
|
|
|
|
return [
|
|
'token' => $token,
|
|
'question' => 'What is ' . $a . ' + ' . $b . '?',
|
|
];
|
|
}
|
|
|
|
public static function verify(string $token, string $answer): bool
|
|
{
|
|
if (session_status() !== PHP_SESSION_ACTIVE || $token === '') {
|
|
return false;
|
|
}
|
|
|
|
$challenge = $_SESSION['captcha'][$token] ?? null;
|
|
unset($_SESSION['captcha'][$token]);
|
|
if (!is_array($challenge) || (int) ($challenge['expires'] ?? 0) < time()) {
|
|
return false;
|
|
}
|
|
|
|
return trim($answer) === (string) ($challenge['answer'] ?? '');
|
|
}
|
|
}
|