PHP WebShell

Текущая директория: /var/www/bitcardoApp/models/auth

Просмотр файла: reset_process.php

<?php
// models/auth/reset_process.php — Verify code, set new password, revoke sessions
require_once __DIR__ . '/../../config/bootstrap.php';

// Optional libs
$rateLib  = __DIR__ . '/../../lib/rate_limit.php';
if (file_exists($rateLib)) require_once $rateLib;
$sessionLib = __DIR__ . '/../../lib/session.php';
if (file_exists($sessionLib)) require_once $sessionLib;

// Helpers
function back_with($arr){
  $_SESSION['flash'] = $arr;
  header('Location: /auth/reset.php'); exit;
}
function rl_on() { return function_exists('is_enabled') ? is_enabled('rate_limit_enabled', true) : true; }
function norm_login($login){
  return function_exists('rl_norm_login') ? rl_norm_login($login) : strtolower(trim($login));
}

// CSRF
if (!isset($_POST['csrf'], $_SESSION['csrf']) || !hash_equals($_SESSION['csrf'], $_POST['csrf'])) {
  back_with(['error' => 'Session expired. Please try again.']);
}

// Input
$login = trim($_POST['login'] ?? '');
$code  = preg_replace('/\D+/', '', $_POST['code'] ?? '');
$pass1 = (string)($_POST['password'] ?? '');
$pass2 = (string)($_POST['password2'] ?? '');

if ($login === '' || $code === '') back_with(['error' => 'Enter your email/phone and code.']);
if (strlen($code) !== 6) back_with(['error' => 'Invalid code.']);
if ($pass1 === '' || strlen($pass1) < 8) back_with(['error' => 'Use at least 8 characters for your new password.']);
if (!hash_equals($pass1, $pass2)) back_with(['error' => 'Passwords do not match.']);

$loginTok = norm_login($login);

// Rate limit: verification attempts per login token
if (rl_on() && function_exists('rl_check_and_inc')) {
  // 8 attempts / 15 mins; lock for 15 mins
  $r = rl_check_and_inc($conn, 'reset_verify', $loginTok, 8, 900, 900);
  if (!$r['ok']) back_with(['error' => "Too many attempts. Try again in {$r['locked_for']}s."]);
}

// Find user
$stmt = $conn->prepare("SELECT user_id, email FROM users WHERE email=? OR phone=? LIMIT 1");
$stmt->bind_param('ss', $login, $login);
$stmt->execute();
$stmt->bind_result($uid, $email);
$found = $stmt->fetch();
$stmt->close();

// Generic error to avoid enumeration
if (!$found || !$uid) back_with(['error' => 'Invalid or expired code. Request a new one and try again.']);

// Load latest unconsumed reset OTP
$q = $conn->prepare("SELECT uotp_id, token_hash, expires_at
                     FROM user_otps
                     WHERE user_id=? AND channel='reset' AND consumed_at IS NULL
                     ORDER BY uotp_id DESC LIMIT 1");
$q->bind_param('i', $uid);
$q->execute();
$q->bind_result($id, $tokenHash, $expiresAt);
$has = $q->fetch();
$q->close();

if (!$has) back_with(['error' => 'Invalid or expired code. Request a new one and try again.']);

if (new DateTimeImmutable($expiresAt) < new DateTimeImmutable()) {
  $u = $conn->prepare("UPDATE user_otps SET consumed_at=NOW() WHERE uotp_id=?");
  $u->bind_param('i', $id); $u->execute(); $u->close();
  back_with(['error' => 'Code expired. Request a new one.']);
}

if (!hash_equals($tokenHash ?? '', hash('sha256', $code))) {
  // bump attempts for telemetry (optional)
  $a = $conn->prepare("UPDATE user_otps SET attempts=LEAST(attempts+1, 250) WHERE uotp_id=?");
  $a->bind_param('i', $id); $a->execute(); $a->close();
  back_with(['error' => 'Invalid code.']);
}

// Consume OTP
$c = $conn->prepare("UPDATE user_otps SET consumed_at=NOW() WHERE uotp_id=?");
$c->bind_param('i', $id); $c->execute(); $c->close();

// Update password
$newHash = password_hash($pass1, PASSWORD_DEFAULT);
$u = $conn->prepare("UPDATE users SET password_hash=?, updated_at=NOW() WHERE user_id=?");
$u->bind_param('si', $newHash, $uid);
$u->execute();
$u->close();

// Revoke all existing remembered sessions for this user
$conn->query("UPDATE user_sessions SET revoked_at=NOW() WHERE user_id=".(int)$uid);

// Clear remember-me cookie if supported
if (function_exists('session_revoke_cookie')) {
  session_revoke_cookie($conn);
}

/**
 * Log out this runtime session safely, then set a flash message.
 * We avoid session_destroy() since we want to set flash; instead:
 *  - wipe session data,
 *  - regenerate ID (to drop any fixed session),
 *  - set flash in the new, clean session,
 *  - redirect.
 */
$_SESSION = [];
session_regenerate_id(true); // new empty session
$_SESSION['flash'] = ['ok' => 'Your password has been reset. Please log in with your new password.'];

// Optionally clear any prefill used in the reset form
unset($_SESSION['prefill_login']);

header('Location: /auth/login.php');
exit;

Выполнить команду


Для локальной разработки. Не используйте в интернете!