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'; ?>
Выполнить команду
Для локальной разработки. Не используйте в интернете!