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.
111 lines
3.6 KiB
PHP
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;
|