Файл: pages/account/pass.php
Строк: 126
<?php
// Physical module map v50.6: public URL stays clean while code lives in /pages.
if (!defined('GAME_ROOT')) define('GAME_ROOT', dirname(__DIR__, 2));
chdir(GAME_ROOT);
if (PHP_SAPI !== 'cli') { $_SERVER['PHP_SELF']='/pass.php'; $_SERVER['SCRIPT_NAME']='/pass.php'; }
/* Обновление игры под PHP 8.4 / PDO, безопасность и оптимизация: by Метриум (Стэлп) */
include './system/common.php';
include './system/functions.php';
include './system/user.php';
$title='Восстановление доступа';
include './system/h.php';
if ($user) { header('Location: /'); exit; }
$action=(string)($_GET['action'] ?? 'request');
$error=''; $ok='';
if ($_SERVER['REQUEST_METHOD']==='POST') {
security_require_csrf();
if (!security_honeypot_ok()) { http_response_code(400); exit('Действие не принято.'); }
if (!security_rate_limit('password-reset', 5, 3600)) { http_response_code(429); exit('Слишком много попыток. Попробуйте позже.'); }
}
if ($action==='request' && $_SERVER['REQUEST_METHOD']==='POST') {
$id=(int)($_POST['id'] ?? 0);
$email=trim((string)($_POST['email'] ?? ''));
$account=$id>0 ? db_fetch_one('SELECT `id`,`email`,`password`,`login` FROM `users` WHERE `id`=:id LIMIT 1',['id'=>$id]) : null;
// Одинаковый ответ не раскрывает существование аккаунта.
$ok='Если ID и e-mail совпадают с аккаунтом, ссылка для смены пароля будет отправлена.';
if($account && filter_var($email,FILTER_VALIDATE_EMAIL) && hash_equals(strtolower((string)$account['email']),strtolower($email))) {
$expires=time()+1800;
$token=security_password_reset_token($account,$expires);
$scheme=(!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS']!=='off')?'https':'http';
$host=preg_replace('/[^a-z0-9.:-]/i','',(string)($_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? ''));
$link=$scheme.'://'.$host.'/pass?action=reset&id='.(int)$account['id'].'&token='.rawurlencode($token);
$subject='Восстановление доступа';
$body="Здравствуйте. Для смены пароля персонажа ".(string)$account['login']." откройте ссылку (действует 30 минут):n".$link."nnЕсли вы не запрашивали восстановление, просто проигнорируйте письмо.";
@mail((string)$account['email'],$subject,$body,"Content-Type: text/plain; charset=UTF-8rn");
}
}
if ($action==='reset') {
$id=(int)($_GET['id'] ?? $_POST['id'] ?? 0);
$token=(string)($_GET['token'] ?? $_POST['token'] ?? '');
$account=$id>0 ? db_fetch_one('SELECT `id`,`email`,`password`,`login` FROM `users` WHERE `id`=:id LIMIT 1',['id'=>$id]) : null;
if(!$account || !security_password_reset_verify($token,$account)) {
$error='Ссылка восстановления недействительна или истекла.';
} elseif($_SERVER['REQUEST_METHOD']==='POST') {
$p1=(string)($_POST['password'] ?? ''); $p2=(string)($_POST['password2'] ?? '');
if(strlen($p1)<8) $error='Пароль должен содержать не менее 8 символов.';
elseif(!hash_equals($p1,$p2)) $error='Пароли не совпадают.';
else {
db_execute('UPDATE `users` SET `password`=:p WHERE `id`=:id',['p'=>security_password_hash($p1),'id'=>$id]);
security_auth_login($id); session_regenerate_id(true);
header('Location: /'); exit;
}
}
}
?>
<div class="head center"><?=htmlspecialchars($title,ENT_QUOTES,'UTF-8')?></div>
<div class="line"></div>
<?php if($error): ?><div class="error center"><?=htmlspecialchars($error,ENT_QUOTES,'UTF-8')?></div><?php endif; ?>
<?php if($ok): ?><div class="ok center"><?=htmlspecialchars($ok,ENT_QUOTES,'UTF-8')?></div><?php endif; ?>
<?php if($action==='reset' && empty($error)): ?>
<form method="post" action="/pass?action=reset">
<?=security_csrf_field()?>
<input type="hidden" name="id" value="<?=(int)$id?>"><input type="hidden" name="token" value="<?=htmlspecialchars($token,ENT_QUOTES,'UTF-8')?>">
<div class="block_zero center">Новый пароль:<br><input class="text medium-text" type="password" name="password" autocomplete="new-password" required minlength="8"><br>
Повторите пароль:<br><input class="text medium-text" type="password" name="password2" autocomplete="new-password" required minlength="8"><br>
<button class="label" type="submit">Сменить пароль</button></div></form>
<?php elseif($action!=='reset'): ?>
<form method="post" action="/pass?action=request">
<?=security_csrf_field()?>
<div style="display:none"><input name="website" tabindex="-1" autocomplete="off"></div>
<div class="block_zero center">ID персонажа:<br><input class="text medium-text" type="number" name="id" min="1" required><br>
E-mail аккаунта:<br><input class="text medium-text" type="email" name="email" maxlength="190" required autocomplete="email"><br>
<button class="label" type="submit">Восстановить доступ</button></div></form>
<?php endif; ?>
<div class="block_zero center"><a href="/">На главную</a></div>
<?php include './system/f.php'; ?>