PHP WebShell
Текущая директория: /var/www/bitcardoApp/lib
Просмотр файла: session.php
<?php
// lib/session.php — resilient remember-me with duration support.
// Works with different column namings (selector vs remember_selector).
/* ---------- helpers ---------- */
if (!function_exists('base64url')) {
function base64url(string $bin): string {
return rtrim(strtr(base64_encode($bin), '+/', '-_'), '=');
}
}
if (!function_exists('rm_cookie_name')) {
function rm_cookie_name(): string {
return 'bc_rm';
}
}
if (!function_exists('rm_set_cookie')) {
function rm_set_cookie(string $value, int $days = 30): void {
setcookie(
rm_cookie_name(),
$value,
[
'expires' => time() + 60 * 60 * 24 * $days,
'path' => '/',
'domain' => 'wallet.bitcardo.com', // adjust if needed
'secure' => !empty($_SERVER['HTTPS']),
'httponly' => true,
'samesite' => 'Lax',
]
);
$_COOKIE[rm_cookie_name()] = $value;
}
}
if (!function_exists('rm_clear_cookie')) {
function rm_clear_cookie(): void {
setcookie(
rm_cookie_name(),
'',
[
'expires' => time() - 3600,
'path' => '/',
'domain' => 'wallet.bitcardo.com',
'secure' => !empty($_SERVER['HTTPS']),
'httponly' => true,
'samesite' => 'Lax',
]
);
unset($_COOKIE[rm_cookie_name()]);
}
}
/* ---------- schema detection (cached) ---------- */
if (!function_exists('usess_schema')) {
function usess_schema(mysqli $conn): array {
static $cache;
if ($cache !== null) return $cache;
$have = [
'selector' => false,
'validator_hash' => false,
'remember_selector' => false,
'remember_validator_hash' => false,
'is_remembered' => false,
'expires_at' => false,
'revoked_at' => false,
'last_seen_at' => false,
'ip_address' => false,
'user_agent' => false,
];
$res = $conn->query("SHOW COLUMNS FROM user_sessions");
if ($res) {
while ($col = $res->fetch_assoc()) {
$name = strtolower($col['Field']);
if (array_key_exists($name, $have)) {
$have[$name] = true;
}
}
$res->close();
}
return $cache = $have;
}
}
/**
* Resolve which columns to use for selector/validator based on schema.
* Returns:
* ['selector' => 'remember_selector' or 'selector',
* 'validator' => 'remember_validator_hash' or 'validator_hash']
*/
if (!function_exists('usess_resolve_columns')) {
function usess_resolve_columns(array $sch): array {
$selectorCol = null;
$validatorCol = null;
if (!empty($sch['remember_selector'])) {
$selectorCol = 'remember_selector';
} elseif (!empty($sch['selector'])) {
$selectorCol = 'selector';
}
if (!empty($sch['remember_validator_hash'])) {
$validatorCol = 'remember_validator_hash';
} elseif (!empty($sch['validator_hash'])) {
$validatorCol = 'validator_hash';
}
return [
'selector' => $selectorCol,
'validator' => $validatorCol,
];
}
}
/* ---------- main API ---------- */
if (!function_exists('session_record_create')) {
function session_record_create(
mysqli $conn,
int $userId,
bool $remember = false,
int $durationSeconds = 2592000 // default 30 days for old callers
): void {
$phpSid = session_id();
$ip = $_SERVER['REMOTE_ADDR'] ?? '';
$ua = substr($_SERVER['HTTP_USER_AGENT'] ?? '', 0, 255);
$now = new DateTimeImmutable();
$nowSql = $now->format('Y-m-d H:i:s');
// Bound duration
if ($durationSeconds < 0) {
$durationSeconds = 0;
}
$MAX = 30 * 24 * 60 * 60;
if ($durationSeconds > $MAX) {
$durationSeconds = $MAX;
}
$sch = usess_schema($conn);
$cols = usess_resolve_columns($sch);
$selectorCol = $cols['selector']; // 'remember_selector' or 'selector'
$validatorCol = $cols['validator']; // 'remember_validator_hash' or 'validator_hash'
// Can this schema support remember-me?
$rememberCapable = (
!empty($selectorCol) &&
!empty($validatorCol) &&
!empty($sch['is_remembered']) &&
!empty($sch['expires_at'])
);
if (!$rememberCapable) {
$remember = false;
}
if ($remember && $durationSeconds > 0) {
// Generate selector + validator
$selector = base64url(random_bytes(16)); // public part
$validator = base64url(random_bytes(32)); // secret part
$valHash = hash('sha256', $validator);
// Exact DB expiry in seconds
$expiresAtObj = $now->modify('+' . $durationSeconds . ' seconds');
$expiresAt = $expiresAtObj->format('Y-m-d H:i:s');
$cookieVal = $selector . ':' . $validator;
$cookieDays = max(1, (int)ceil($durationSeconds / 86400));
rm_set_cookie($cookieVal, $cookieDays);
// Insert full remember row with dynamic columns
$sql = "INSERT INTO user_sessions
(user_id, php_session_id, {$selectorCol}, {$validatorCol}, is_remembered,
ip_address, user_agent, last_seen_at, expires_at)
VALUES (?, ?, ?, ?, 1, ?, ?, ?, ?)";
$stmt = $conn->prepare($sql);
if ($stmt) {
$stmt->bind_param(
'isssssss',
$userId,
$phpSid,
$selector,
$valHash,
$ip,
$ua,
$nowSql,
$expiresAt
);
$stmt->execute();
$stmt->close();
}
return;
}
// No remember-me → minimal insert
if ($sch['ip_address'] && $sch['user_agent'] && $sch['last_seen_at']) {
$sql = "INSERT INTO user_sessions
(user_id, php_session_id, ip_address, user_agent, last_seen_at)
VALUES (?, ?, ?, ?, ?)";
$stmt = $conn->prepare($sql);
if ($stmt) {
$stmt->bind_param('issss', $userId, $phpSid, $ip, $ua, $nowSql);
$stmt->execute();
$stmt->close();
}
} else {
$sql = "INSERT INTO user_sessions (user_id, php_session_id) VALUES (?, ?)";
$stmt = $conn->prepare($sql);
if ($stmt) {
$stmt->bind_param('is', $userId, $phpSid);
$stmt->execute();
$stmt->close();
}
}
}
}
/**
* Try to auto-login from bc_rm cookie.
*/
if (!function_exists('session_try_autologin')) {
function session_try_autologin(mysqli $conn): bool {
if (!empty($_SESSION['user_id'])) return true;
$sch = usess_schema($conn);
$cols = usess_resolve_columns($sch);
$selectorCol = $cols['selector'];
$validatorCol = $cols['validator'];
$cookie = $_COOKIE[rm_cookie_name()] ?? '';
if (!$cookie || strpos($cookie, ':') === false) return false;
// Table cannot support remember-me lookup
if (empty($selectorCol) || empty($validatorCol) || !$sch['is_remembered'] || !$sch['expires_at']) {
rm_clear_cookie();
return false;
}
list($selector, $validator) = explode(':', $cookie, 2);
$selector = substr($selector, 0, 255);
$valHash = hash('sha256', $validator);
// Look up by selector + validator hash
$sql = "SELECT user_id, expires_at, revoked_at
FROM user_sessions
WHERE {$selectorCol} = ? AND {$validatorCol} = ? AND is_remembered = 1
ORDER BY expires_at DESC
LIMIT 1";
$stmt = $conn->prepare($sql);
if (!$stmt) {
rm_clear_cookie();
return false;
}
$stmt->bind_param('ss', $selector, $valHash);
$stmt->execute();
$stmt->bind_result($userId, $expiresAt, $revokedAt);
$found = $stmt->fetch();
$stmt->close();
if (!$found || $revokedAt) {
rm_clear_cookie();
return false;
}
// Expired?
if ($expiresAt && new DateTimeImmutable($expiresAt) < new DateTimeImmutable()) {
rm_clear_cookie();
// best-effort mark revoked by selector
$uSql = "UPDATE user_sessions
SET revoked_at = NOW()
WHERE {$selectorCol} = ? AND {$validatorCol} = ? AND is_remembered = 1";
$u = $conn->prepare($uSql);
if ($u) {
$u->bind_param('ss', $selector, $valHash);
$u->execute();
$u->close();
}
return false;
}
// Load user basics
$u = $conn->prepare("SELECT email, first_name, last_name FROM users WHERE user_id = ? LIMIT 1");
if (!$u) {
rm_clear_cookie();
return false;
}
$u->bind_param('i', $userId);
$u->execute();
$u->bind_result($email, $first, $last);
if (!$u->fetch()) {
$u->close();
rm_clear_cookie();
return false;
}
$u->close();
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
session_regenerate_id(true);
$_SESSION['user_id'] = (int)$userId;
$_SESSION['email'] = $email;
$_SESSION['first_name'] = $first ?? '';
$_SESSION['last_name'] = $last ?? '';
$_SESSION['loggedIn'] = true;
// Update last_seen_at if present
if ($sch['last_seen_at']) {
$updSql = "UPDATE user_sessions
SET last_seen_at = NOW()
WHERE {$selectorCol} = ? AND {$validatorCol} = ? AND is_remembered = 1";
$t = $conn->prepare($updSql);
if ($t) {
$t->bind_param('ss', $selector, $valHash);
$t->execute();
$t->close();
}
}
return true;
}
}
/**
* Revoke remember-me cookie (and mark DB rows revoked where possible).
*/
if (!function_exists('session_revoke_cookie')) {
function session_revoke_cookie(mysqli $conn): void {
$cookie = $_COOKIE[rm_cookie_name()] ?? '';
if (!$cookie || strpos($cookie, ':') === false) {
rm_clear_cookie();
return;
}
$sch = usess_schema($conn);
$cols = usess_resolve_columns($sch);
$selectorCol = $cols['selector'];
if (!empty($selectorCol)) {
list($selector,) = explode(':', $cookie, 2);
$selector = substr($selector, 0, 255);
$sql = "UPDATE user_sessions
SET revoked_at = NOW()
WHERE {$selectorCol} = ? AND is_remembered = 1";
$u = $conn->prepare($sql);
if ($u) {
$u->bind_param('s', $selector);
$u->execute();
$u->close();
}
}
rm_clear_cookie();
}
}
Выполнить команду
Для локальной разработки. Не используйте в интернете!