Files
wow-registrace/public/register.php
T
vojta 2b9a673912 Initial commit: AzerothCore registration site
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.
2026-08-05 11:41:24 +02:00

111 lines
3.6 KiB
PHP

<?php
declare(strict_types=1);
$config = require __DIR__ . '/../includes/bootstrap.php';
function fail(array $errors, array $old = []): never
{
$_SESSION['flash_errors'] = $errors;
$_SESSION['flash_old'] = $old;
header('Location: registrace.php');
exit;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: registrace.php');
exit;
}
$old = [
'username' => trim((string) ($_POST['username'] ?? '')),
'email' => trim((string) ($_POST['email'] ?? '')),
];
if (!csrf_verify($_POST['csrf_token'] ?? null)) {
fail(['Neplatný nebo vypršelý formulář, zkus to prosím znovu.']);
}
// Honeypot: boti pole často vyplní. Předstíráme úspěch, aby se bot
// nedozvěděl, že byl odhalen, ale žádná data se nezapíšou.
if (honeypot_triggered()) {
$_SESSION['flash_success'] = 'Účet byl úspěšně vytvořen. Nyní se můžeš přihlásit do hry.';
header('Location: registrace.php');
exit;
}
$ip = client_ip();
$rateLimiter = new RateLimiter(__DIR__ . '/../data/ratelimit.sqlite', ...array_values($config['rate_limit']));
if ($rateLimiter->tooManyAttempts($ip)) {
fail(['Příliš mnoho pokusů o registraci z tvé IP adresy. Zkus to prosím později.'], $old);
}
$username = $old['username'];
$email = $old['email'];
$password = (string) ($_POST['password'] ?? '');
$passwordConfirm = (string) ($_POST['password_confirm'] ?? '');
$errors = [];
$errors = array_merge($errors, validate_username($username, $config['rules']));
$errors = array_merge($errors, validate_email($email));
$errors = array_merge($errors, validate_password($password, $config['rules']));
if ($password !== $passwordConfirm) {
$errors[] = 'Zadaná hesla se neshodují.';
}
$turnstileSecret = $config['turnstile']['secret_key'];
if ($turnstileSecret !== '') {
$turnstileToken = (string) ($_POST['cf-turnstile-response'] ?? '');
if (!Turnstile::verify($turnstileToken, $turnstileSecret, $ip)) {
$errors[] = 'Ověření „nejsem robot“ se nezdařilo, zkus to prosím znovu.';
}
}
if (!empty($errors)) {
// Neúspěšný pokus se počítá do rate limitu, aby útočník nemohl
// zkoušet donekonečna jen proto, že validace selhala.
$rateLimiter->recordAttempt($ip);
fail($errors, $old);
}
$pdo = Database::connection();
$stmt = $pdo->prepare('SELECT id FROM account WHERE username = :username');
$stmt->execute([':username' => strtoupper($username)]);
if ($stmt->fetch() !== false) {
$rateLimiter->recordAttempt($ip);
fail(['Toto uživatelské jméno už je obsazené.'], $old);
}
$srp = Srp6::makeRegistrationData($username, $password);
try {
$insert = $pdo->prepare(
'INSERT INTO account (username, salt, verifier, email, reg_mail, joindate, last_ip)
VALUES (:username, :salt, :verifier, :email, :reg_mail, NOW(), :last_ip)'
);
$insert->execute([
':username' => strtoupper($username),
':salt' => $srp['salt'],
':verifier' => $srp['verifier'],
':email' => $email,
':reg_mail' => $email,
':last_ip' => $ip,
]);
} catch (PDOException $e) {
$rateLimiter->recordAttempt($ip);
if ($e->getCode() === '23000') {
fail(['Toto uživatelské jméno už je obsazené.'], $old);
}
error_log('Registration insert failed: ' . $e->getMessage());
fail(['Registraci se nepodařilo dokončit, zkus to prosím později.'], $old);
}
$rateLimiter->recordAttempt($ip);
$_SESSION['flash_success'] = 'Účet byl úspěšně vytvořen. Nyní se můžeš přihlásit do hry.';
unset($_SESSION['csrf_token']);
header('Location: registrace.php');
exit;