83 lines
2.6 KiB
PHP
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();
|
|
}
|
|
}
|