Files
Critters/session.php
T
pauljd 35d187b4c2
pauljd/davidson-ci: Davidson CI / quality (push) Successful in 13s
pauljd/davidson-ci: Davidson CI / ai-review (push) Successful in 1m0s
further Davidson CI testing
2026-09-09 22:55:59 +01:00

83 lines
2.6 KiB
PHP

<?php
function send_app_csp_header(): void
{
if (headers_sent()) {
return;
}
header('Content-Security-Policy: ' . implode(' ', [
"script-src 'self' 'unsafe-inline' https://unpkg.com https://cdn.paddle.com;",
"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;",
"font-src https://fonts.gstatic.com;",
"img-src 'self' data:;",
"connect-src 'self' https://cdn.paddle.com https://api.paddle.com https://sandbox-api.paddle.com;",
"frame-src https://buy.paddle.com https://sandbox-buy.paddle.com;",
"object-src 'none';",
"base-uri 'self';",
"frame-ancestors 'none';",
]), true, 0);
}
function start_app_session(): void
{
send_app_csp_header();
if (session_status() === PHP_SESSION_ACTIVE) {
return;
}
$isHttps = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off')
|| (($_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '') === 'https')
|| (($_SERVER['SERVER_PORT'] ?? '') === '443');
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'secure' => $isHttps,
'httponly' => true,
'samesite' => 'Strict',
]);
session_start();
}
function csrf_token(): string
{
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
return $_SESSION['csrf_token'];
}
function csrf_field(): string
{
return '<input type="hidden" name="csrf_token" value="' . htmlspecialchars(csrf_token(), ENT_QUOTES, 'UTF-8') . '">';
}
function csrf_verify(?string $token): bool
{
return is_string($token) && $token !== '' && isset($_SESSION['csrf_token']) && hash_equals($_SESSION['csrf_token'], $token);
}
// Logs the session out if the user's session_version has moved on since login (e.g. a
// password reset bumped it), so a stolen/old session cookie stops working immediately
// instead of staying valid until it naturally expires. Call this right after
// start_app_session() + getDbConnection() on any page that trusts $_SESSION['user_id'].
function enforce_session_version(PDO $pdo): void
{
if (!isset($_SESSION['user_id'])) {
return;
}
$stmt = $pdo->prepare('SELECT session_version FROM users WHERE id = :id');
$stmt->execute(['id' => $_SESSION['user_id']]);
$currentVersion = $stmt->fetchColumn();
$sessionVersionMatches = $currentVersion !== false
&& isset($_SESSION['session_version'])
&& (int) $currentVersion === (int) $_SESSION['session_version'];
if (!$sessionVersionMatches) {
$_SESSION = [];
session_destroy();
}
}