PHP WebShell

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

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

<?php
// user/account/close_account.php
require_once __DIR__ . '/../../config/bootstrap.php';
require_once __DIR__ . '/../../lib/csrf.php';

if (empty($_SESSION['user_id'])) {
  header('Location: /auth/login.php');
  exit;
}

$userId = (int)$_SESSION['user_id'];

/**
 * Page lock (password re-auth)
 */
$REAUTH_TTL = 10 * 60; // 10 minutes
$REAUTH_KEY = 'reauth_close_account_ts';

if (empty($_SESSION['csrf_close_account'])) {
  $_SESSION['csrf_close_account'] = bin2hex(random_bytes(16));
}

function needs_reauth_close(int $ttl, string $key): bool {
  if (empty($_SESSION[$key])) return true;
  $ts = (int)$_SESSION[$key];
  return ($ts <= 0) || ((time() - $ts) > $ttl);
}

function get_password_hash(mysqli $conn, int $userId): ?string {
  $hash = null;
  if ($st = $conn->prepare("SELECT password_hash FROM users WHERE user_id=? LIMIT 1")) {
    $st->bind_param('i', $userId);
    $st->execute();
    $st->bind_result($hash);
    $st->fetch();
    $st->close();
  }
  return $hash ?: null;
}

function table_exists(mysqli $conn, string $tableName): bool {
  // Safe: uses information_schema
  $db = '';
  if ($r = $conn->query("SELECT DATABASE() AS db")) {
    $row = $r->fetch_assoc();
    $db = $row['db'] ?? '';
    $r->free();
  }
  if ($db === '') return false;

  $exists = 0;
  $sql = "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema=? AND table_name=? LIMIT 1";
  if ($st = $conn->prepare($sql)) {
    $st->bind_param('ss', $db, $tableName);
    $st->execute();
    $st->bind_result($exists);
    $st->fetch();
    $st->close();
  }
  return ((int)$exists) > 0;
}

$lockError = '';
$success = '';
$errors = [];

// Handle re-auth submit
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'reauth') {
  $csrf = (string)($_POST['csrf'] ?? '');
  $pwd  = (string)($_POST['password'] ?? '');

  if (!hash_equals((string)($_SESSION['csrf_close_account'] ?? ''), $csrf)) {
    $lockError = "Security check failed. Please try again.";
  } else {
    $hash = get_password_hash($conn, $userId);
    if (!$hash) {
      $lockError = "Unable to verify your account right now.";
    } elseif (!password_verify($pwd, $hash)) {
      $lockError = "Incorrect password. Please try again.";
    } else {
      $_SESSION[$REAUTH_KEY] = time();
      header("Location: /user/account/close_account.php");
      exit;
    }
  }
}

// Gate: if not recently authenticated, show password prompt
if (needs_reauth_close($REAUTH_TTL, $REAUTH_KEY)) {
  include __DIR__ . '/../common/header.php';
  ?>
  <style>
    .card-soft { border:1px solid rgba(7,98,137,.12); border-radius:12px; background:#fff; box-shadow:0 8px 24px rgba(7,98,137,.06); }
    .back-btn-sm{ padding:4px 10px; font-size:.80rem; border-radius:10px; }
  </style>

  <div class="container mt-3">
    <div class="row">
      <? include __DIR__ . '/../common/nav.php'; ?>

      <main class="col-md-9 col-lg-10 px-md-5 mb-5">
        <? include __DIR__ . '/../common/page-header.php'; ?>

        <div class="container my-5 px-md-5 ms-md-4">
          <div class="row justify-content-center">
            <div class="col-12 col-md-7 col-lg-5">
              <div class="card-soft p-3">
                <div class="d-flex justify-content-between align-items-start">
                  <div>
                    <h5 class="mb-1">Confirm it’s you</h5>
                    <div class="text-muted small">Enter your password to continue to Close account.</div>
                  </div>
                  <a href="/user/account/account.php" class="btn btn-outline-secondary btn-sm back-btn-sm">
                    <i class="bi bi-arrow-left"></i> Back
                  </a>
                </div>

                <?php if ($lockError): ?>
                  <div class="alert alert-danger mt-3 mb-0"><?= htmlspecialchars($lockError) ?></div>
                <?php endif; ?>

                <form method="post" class="mt-3">
                  <input type="hidden" name="action" value="reauth">
                  <input type="hidden" name="csrf" value="<?= htmlspecialchars((string)$_SESSION['csrf_close_account']) ?>">

                  <label class="form-label">Password</label>
                  <input type="password" name="password" class="form-control" placeholder="Enter your password" required>

                  <button type="submit" class="btn btn-dark w-100 mt-3">Continue</button>
                  <div class="text-muted small mt-2">This step helps keep your account safe.</div>
                </form>
              </div>
            </div>
          </div>
        </div>

      </main>
    </div>
  </div>

  <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
  <? include __DIR__ . '/../common/footer.php'; ?>
  <?php
  exit;
}

// --- table existence check (prevents fatal) ---
$closeTableOk = table_exists($conn, 'user_close_requests');

if (empty($_SESSION['csrf_close_submit'])) {
  $_SESSION['csrf_close_submit'] = bin2hex(random_bytes(16));
}

// Handle submit close request
if ($_SERVER['REQUEST_METHOD'] === 'POST' && ($_POST['action'] ?? '') === 'submit_close') {

  if (!$closeTableOk) {
    $errors[] = "Close account requests are not available yet. Please contact support.";
  } else {
    $csrf = (string)($_POST['csrf'] ?? '');
    if (!hash_equals((string)($_SESSION['csrf_close_submit'] ?? ''), $csrf)) {
      $errors[] = "Security check failed. Please refresh and try again.";
    } else {
      $reason = trim((string)($_POST['reason'] ?? ''));
      $note   = trim((string)($_POST['note'] ?? ''));

      if ($reason === '') $errors[] = "Please select a reason.";

      // Prevent duplicate pending requests
      $hasPending = 0;
      if ($st = $conn->prepare("SELECT COUNT(*) FROM user_close_requests WHERE user_id=? AND status='pending'")) {
        $st->bind_param('i', $userId);
        $st->execute();
        $st->bind_result($hasPending);
        $st->fetch();
        $st->close();
      }

      if (!$errors && $hasPending > 0) {
        $errors[] = "You already have a pending close account request.";
      }

      if (empty($errors)) {
        if ($st = $conn->prepare("INSERT INTO user_close_requests (user_id, reason, note, status, created_at) VALUES (?,?,?,'pending',NOW())")) {
          $st->bind_param('iss', $userId, $reason, $note);
          if ($st->execute()) {
            $success = "Your close account request has been submitted.";
          } else {
            $errors[] = "Unable to submit your request right now.";
          }
          $st->close();
        } else {
          $errors[] = "Unable to submit your request right now.";
        }
      }
    }
  }
}

// Fetch pending request (if table exists)
$pending = null;
if ($closeTableOk) {
  if ($st = $conn->prepare("SELECT req_id, reason, note, status, created_at FROM user_close_requests WHERE user_id=? AND status='pending' ORDER BY created_at DESC LIMIT 1")) {
    $st->bind_param('i', $userId);
    $st->execute();
    $res = $st->get_result();
    $pending = $res ? $res->fetch_assoc() : null;
    $st->close();
  }
}

include __DIR__ . '/../common/header.php';
?>
<style>
  .card-soft { border:1px solid rgba(7,98,137,.12); border-radius:12px; background:#fff; box-shadow:0 8px 24px rgba(7,98,137,.06); }
  .muted { color:#6b7280; }
  .back-btn-sm{ padding:4px 10px; font-size:.80rem; border-radius:10px; }
</style>

<div class="container mt-3">
  <div class="row">

    <? include __DIR__ . '/../common/nav.php'; ?>

    <main class="col-md-9 col-lg-10 px-md-5 mb-5">
      <? include __DIR__ . '/../common/page-header.php'; ?>

      <div class="container my-5 px-md-5 ms-md-4">
        <div class="offset-md-3 col-md-6">
          <div class="d-flex align-items-center justify-content-between mb-3">
            <div>
              <h5 class="mb-0">Close account</h5>
              <div class="muted small">Request to close your Bitcardo account.</div>
            </div>
            <a href="/user/account/account.php" class="btn btn-outline-secondary btn-sm back-btn-sm">
              <i class="bi bi-arrow-left"></i> Back
            </a>
          </div>

          <?php if (!$closeTableOk): ?>
            <div class="alert alert-warning">
              Close account requests are not available yet on this environment.
              Please run the database migration for <strong>user_close_requests</strong>.
            </div>
          <?php endif; ?>

          <?php if ($success): ?>
            <div class="alert alert-success"><?= htmlspecialchars($success) ?></div>
          <?php endif; ?>

          <?php if (!empty($errors)): ?>
            <div class="alert alert-danger">
              <strong>Please fix the following:</strong>
              <ul class="mb-0">
                <?php foreach ($errors as $e): ?><li><?= htmlspecialchars($e) ?></li><?php endforeach; ?>
              </ul>
            </div>
          <?php endif; ?>

          <?php if ($pending): ?>
            <div class="card-soft p-3 mb-3">
              <h6 class="mb-1">Your request is pending</h6>
              <div class="muted small mb-2">Submitted: <?= htmlspecialchars($pending['created_at'] ?? '-') ?></div>
              <div class="small"><strong>Reason:</strong> <?= htmlspecialchars($pending['reason'] ?? '-') ?></div>
              <?php if (!empty($pending['note'])): ?>
                <div class="small mt-1"><strong>Note:</strong> <?= nl2br(htmlspecialchars($pending['note'])) ?></div>
              <?php endif; ?>
              <div class="mt-2">
                <span class="badge bg-warning text-dark">Pending review</span>
              </div>
            </div>
          <?php endif; ?>

          <div class="card-soft p-3">
            <h6 class="mb-2">Before you close your account</h6>
            <ul class="muted small mb-3">
              <li>Make sure your wallet balances are zero.</li>
              <li>Any pending transactions should be completed.</li>
              <li>Closing your account may limit access to your history and receipts.</li>
            </ul>

            <?php if (!$closeTableOk): ?>
              <div class="muted small">This feature is unavailable until the database table is created.</div>
            <?php elseif ($pending): ?>
              <div class="muted small">You already have a pending request. You can’t submit another one right now.</div>
            <?php else: ?>
              <form method="post">
                <input type="hidden" name="action" value="submit_close">
                <input type="hidden" name="csrf" value="<?= htmlspecialchars((string)$_SESSION['csrf_close_submit']) ?>">

                <div class="row g-3">
                  <div class="col-12">
                    <label class="form-label">Reason</label>
                    <select name="reason" class="form-select" required>
                      <option value="">Select a reason</option>
                      <option value="No longer needed">No longer needed</option>
                      <option value="Privacy concerns">Privacy concerns</option>
                      <option value="Too many issues">Too many issues</option>
                      <option value="I have another account">I have another account</option>
                      <option value="Other">Other</option>
                    </select>
                  </div>

                  <div class="col-12">
                    <label class="form-label">Additional note (optional)</label>
                    <textarea name="note" class="form-control" rows="3" placeholder="Tell us more (optional)"></textarea>
                  </div>

                  <div class="col-12">
                    <button class="btn btn-danger w-100">
                      Submit close account request
                    </button>
                  </div>

                  <div class="muted small">
                    We will review your request. Your account is not deleted immediately.
                  </div>
                </div>
              </form>
            <?php endif; ?>
          </div>
        </div>
      </div>
    </main>
  </div>
</div>

<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
<? include __DIR__ . '/../common/footer.php'; ?>

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


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