PHP WebShell

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

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

<?php
declare(strict_types=1);

// Session cookie hardening (must be set before session_start)
session_set_cookie_params([
    'httponly' => true,
    'secure'   => isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off',
    'samesite' => 'Lax',
]);
session_start();

require '../../config/db_config.php'; // uses $conn (MySQLi)

// Show errors during development (remove on production)
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);

/**
 * Generate a unique 12-digit wallet_add and create the user's NGN wallet.
 *
 * @throws RuntimeException on failure
 * @return string The created wallet_add
 */
function createNgnWallet(mysqli $conn, int $userId): string
{
    // If a wallet already exists for NGN, return it (idempotent safety)
    $check = $conn->prepare("SELECT wallet_add FROM user_wallets WHERE user_id = ? AND coin = 'NGN' LIMIT 1");
    $check->bind_param('i', $userId);
    $check->execute();
    $check->bind_result($existingAdd);
    if ($check->fetch()) {
        $check->close();
        return $existingAdd;
    }
    $check->close();

    // Generate unique 12-digit wallet_add
    $walletAdd = '';
    $probe     = $conn->prepare("SELECT 1 FROM user_wallets WHERE wallet_add = ? LIMIT 1");
    do {
        // 12-digit numeric string (leading zeros allowed)
        $walletAdd = str_pad((string)random_int(0, 999999999999), 12, '0', STR_PAD_LEFT);

        $probe->bind_param('s', $walletAdd);
        $probe->execute();
        $probe->store_result();
        $exists = $probe->num_rows > 0;
    } while ($exists);
    $probe->close();

    // Insert NGN wallet
    $insert = $conn->prepare("
        INSERT INTO user_wallets
            (cwallet_id, user_id, wallet_add, bank_name, wallet_qr, coin, icon, balance, type, label, wallet_status, updated_at, created_at)
        VALUES
            (NULL, ?, ?, 'Bitcardo', NULL, 'NGN', 'ngn.png', '0.00', 'fiat', 'Naira Wallet', 'active', NOW(), NOW())
    ");
    $insert->bind_param('is', $userId, $walletAdd);
    $insert->execute();
    $insert->close();

    return $walletAdd;
}

try {
    if (!isset($conn) || !($conn instanceof mysqli)) {
        throw new RuntimeException('Database connection not initialized.');
    }

    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: ../../auth/register.php');
        exit;
    }

    // Gather inputs
    $first             = trim($_POST['first_name'] ?? '');
    $last              = trim($_POST['last_name'] ?? '');
    $emailInput        = trim($_POST['email'] ?? '');
    $phoneInput        = trim($_POST['phone'] ?? '');
    $password          = $_POST['password'] ?? '';
    $recaptchaResponse = $_POST['g-recaptcha-response'] ?? '';

    // Preserve original inputs for form re-fill
    $_SESSION['form_data'] = [
        'first' => $first,
        'last'  => $last,
        'email' => $emailInput,
        'phone' => $phoneInput,
    ];

    // Basic validation
    if ($first === '' || $last === '' || $emailInput === '' || $phoneInput === '' || $password === '') {
        $_SESSION['error'] = 'Please fill in all required fields.';
        header('Location: ../../auth/register.php');
        exit;
    }
    if (!filter_var($emailInput, FILTER_VALIDATE_EMAIL)) {
        $_SESSION['error'] = 'Please enter a valid email address.';
        header('Location: ../../auth/register.php');
        exit;
    }
    if (!preg_match('/^[0-9+\-\s()]{7,20}$/', $phoneInput)) {
        $_SESSION['error'] = 'Please enter a valid phone number.';
        header('Location: ../../auth/register.php');
        exit;
    }
    if (!preg_match('/^(?=.*[A-Z])(?=.*\d).{8,}$/', $password)) {
        $_SESSION['error'] = 'Password must be at least 8 characters, include a number and an uppercase letter.';
        header('Location: ../../auth/register.php');
        exit;
    }

    // Normalize for storage/uniqueness
    function normalize_email($input) {
        $input = trim($input);
        $parts = explode('@', $input, 2);
        if (count($parts) !== 2) return $input; // or handle invalid address
        [$local, $domain] = $parts;
        return $local . '@' . strtolower($domain);
    }

    $email = normalize_email($emailInput);
    $phone = preg_replace('/\D+/', '', $phoneInput); // digits only

    // Lightweight throttling by IP (session-based)
    $ip  = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
    $key = 'reg_attempts_' . $ip;
    $_SESSION[$key] = ($_SESSION[$key] ?? 0) + 1;
    if ($_SESSION[$key] > 10) {
        $_SESSION['error'] = 'Too many attempts. Please try again later.';
        header('Location: ../../auth/register.php');
        exit;
    }

    // reCAPTCHA verification (use constant if available; fallback to your current value)
    $recaptchaSecret = defined('RECAPTCHA_SECRET')
        ? RECAPTCHA_SECRET
        : '6Lf4qEwrAAAAAO_OSKyBcGLPRtxy___IZMTLAwxP';

    if ($recaptchaResponse === '') {
        $_SESSION['error'] = 'Captcha verification failed.';
        header('Location: ../../auth/register.php');
        exit;
    }
    $verifyUrl  = 'https://www.google.com/recaptcha/api/siteverify';
    $postFields = http_build_query([
        'secret'   => $recaptchaSecret,
        'response' => $recaptchaResponse,
        'remoteip' => $_SERVER['REMOTE_ADDR'] ?? null,
    ]);

    $captchaOk = false;
    if (function_exists('curl_init')) {
        $ch = curl_init($verifyUrl);
        curl_setopt_array($ch, [
            CURLOPT_POST           => true,
            CURLOPT_POSTFIELDS     => $postFields,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_TIMEOUT        => 10,
        ]);
        $result = curl_exec($ch);
        curl_close($ch);
        if ($result !== false) {
            $decoded   = json_decode($result, true);
            $captchaOk = !empty($decoded['success']);
        }
    } else {
        $result = @file_get_contents($verifyUrl . '?' . $postFields);
        if ($result !== false) {
            $decoded   = json_decode($result, true);
            $captchaOk = !empty($decoded['success']);
        }
    }

    if (!$captchaOk) {
        $_SESSION['error'] = 'Captcha verification failed.';
        header('Location: ../../auth/register.php');
        exit;
    }

    // Begin transaction for atomic user + wallet creation
    $conn->begin_transaction();

    // Prevent duplicate email/phone (normalized)
    $checkSql = "SELECT user_id FROM `users` WHERE email = ? OR phone = ? LIMIT 1";
    $stmt = $conn->prepare($checkSql);
    $stmt->bind_param('ss', $email, $phone);
    $stmt->execute();
    $stmt->store_result();
    if ($stmt->num_rows > 0) {
        $stmt->close();
        $conn->rollback();
        $_SESSION['error'] = 'An account with this email or phone already exists.';
        header('Location: ../../auth/register.php');
        exit;
    }
    $stmt->close();

    // Hash the password
    $passwordHash = password_hash($password, PASSWORD_DEFAULT);

    // Insert user (with status + created_at)
    $insertUser = $conn->prepare("
        INSERT INTO `users`
            (email, phone, password_hash, first_name, last_name, user_status, created_at)
        VALUES
            (?, ?, ?, ?, ?, 'active', NOW())
    ");
    $insertUser->bind_param('sssss', $email, $phone, $passwordHash, $first, $last);
    $insertUser->execute();
    $newUserId = (int)$conn->insert_id;
    $insertUser->close();

    // Create NGN wallet
    createNgnWallet($conn, $newUserId);

    // All good
    $conn->commit();

    // -------- NEW: make sure challenge page has the identity it needs --------
    session_regenerate_id(true);
    $_SESSION['user_id']    = $newUserId;
    $_SESSION['email']      = $email;        // <-- important for /auth/challenge.php (email OTP fallback)
    $_SESSION['first_name'] = $first ?? '';
    $_SESSION['last_name']  = $last  ?? '';
    $_SESSION['loggedIn']   = true;
    unset($_SESSION['form_data'], $_SESSION['error']);
    // ------------------------------------------------------------------------

    // Allow skipping OTP this first session, and show "Trust this device?" banner
    $_SESSION['first_session_grace'] = 1;
    $_SESSION['show_trust_banner']   = 1;

    // Redirect (keep your current destination)
    header('Location: ../../user/dashboard/index.php');
    exit;

} catch (Throwable $e) {
    if (isset($conn) && $conn instanceof mysqli) {
        // Rollback if a transaction is open
        try { $conn->rollback(); } catch (\Throwable $ignore) {}
    }
    $_SESSION['error'] = 'Registration failed. ' . $e->getMessage();
    header('Location: ../../auth/register.php');
    exit;
}

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


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