2b9a673912
PHP registration page for AzerothCore with SRP6 salt/verifier generation, Cloudflare Turnstile + honeypot + rate limiting anti-spam, and a WoW-themed landing page with client setup instructions.
66 lines
1.9 KiB
PHP
66 lines
1.9 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* File-based (SQLite) rate limiter keyed by IP. Kept separate from the
|
|
* AzerothCore auth database on purpose — this is purely a web-layer
|
|
* anti-spam guard, not game data.
|
|
*/
|
|
final class RateLimiter
|
|
{
|
|
private PDO $db;
|
|
private int $maxAttempts;
|
|
private int $windowSeconds;
|
|
|
|
public function __construct(string $storagePath, int $maxAttempts, int $windowSeconds)
|
|
{
|
|
$this->maxAttempts = $maxAttempts;
|
|
$this->windowSeconds = $windowSeconds;
|
|
|
|
$isNew = !is_file($storagePath);
|
|
$this->db = new PDO('sqlite:' . $storagePath);
|
|
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
|
|
|
if ($isNew) {
|
|
$this->db->exec(
|
|
'CREATE TABLE attempts (
|
|
ip TEXT NOT NULL,
|
|
created_at INTEGER NOT NULL
|
|
)'
|
|
);
|
|
$this->db->exec('CREATE INDEX idx_attempts_ip ON attempts (ip, created_at)');
|
|
}
|
|
}
|
|
|
|
public function tooManyAttempts(string $ip): bool
|
|
{
|
|
$this->cleanup($ip);
|
|
|
|
$stmt = $this->db->prepare('SELECT COUNT(*) FROM attempts WHERE ip = :ip AND created_at > :since');
|
|
$stmt->execute([
|
|
':ip' => $ip,
|
|
':since' => time() - $this->windowSeconds,
|
|
]);
|
|
|
|
return (int) $stmt->fetchColumn() >= $this->maxAttempts;
|
|
}
|
|
|
|
public function recordAttempt(string $ip): void
|
|
{
|
|
$stmt = $this->db->prepare('INSERT INTO attempts (ip, created_at) VALUES (:ip, :now)');
|
|
$stmt->execute([
|
|
':ip' => $ip,
|
|
':now' => time(),
|
|
]);
|
|
}
|
|
|
|
private function cleanup(string $ip): void
|
|
{
|
|
$stmt = $this->db->prepare('DELETE FROM attempts WHERE ip = :ip AND created_at <= :since');
|
|
$stmt->execute([
|
|
':ip' => $ip,
|
|
':since' => time() - $this->windowSeconds,
|
|
]);
|
|
}
|
|
}
|