111 lines
3.2 KiB
PHP
111 lines
3.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/db.inc.php';
|
|
|
|
session_set_cookie_params([
|
|
'httponly' => true,
|
|
'samesite' => 'Strict',
|
|
'secure' => !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off',
|
|
]);
|
|
ini_set('session.use_strict_mode', '1');
|
|
session_start();
|
|
|
|
header('Cache-Control: no-store');
|
|
header('X-Content-Type-Options: nosniff');
|
|
header('X-Frame-Options: DENY');
|
|
header('Referrer-Policy: no-referrer');
|
|
header('Permissions-Policy: camera=(), microphone=(), geolocation=()');
|
|
header("Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self'; base-uri 'self'; form-action 'self'; frame-ancestors 'none'");
|
|
|
|
function db(): PDO
|
|
{
|
|
global $dbConfig;
|
|
$dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=%s', $dbConfig['host'], $dbConfig['port'], $dbConfig['database'], $dbConfig['charset']);
|
|
return new PDO($dsn, $dbConfig['username'], $dbConfig['password'], [
|
|
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
|
|
PDO::ATTR_EMULATE_PREPARES => false,
|
|
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
|
]);
|
|
}
|
|
|
|
$pdo = db();
|
|
|
|
function requireAdmin(): void
|
|
{
|
|
global $pdo;
|
|
if (!isset($_SESSION['admin_id'])) {
|
|
http_response_code(403);
|
|
exit('Zugriff verweigert.');
|
|
}
|
|
$statement = $pdo->prepare('SELECT id FROM admin_users WHERE id = :id AND is_active = 1 LIMIT 1');
|
|
$statement->execute([':id' => (int) $_SESSION['admin_id']]);
|
|
if (!$statement->fetchColumn()) {
|
|
$_SESSION = [];
|
|
session_destroy();
|
|
http_response_code(403);
|
|
exit('Zugriff verweigert.');
|
|
}
|
|
}
|
|
|
|
function e(mixed $value): string
|
|
{
|
|
return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
|
|
}
|
|
|
|
function csrf(): string
|
|
{
|
|
return $_SESSION['admin_csrf'] ??= bin2hex(random_bytes(32));
|
|
}
|
|
|
|
function verifyCsrf(): void
|
|
{
|
|
$token = $_POST['csrf_token'] ?? '';
|
|
if (!is_string($token) || !hash_equals(csrf(), $token)) {
|
|
http_response_code(403);
|
|
exit('Ungültige Anfrage.');
|
|
}
|
|
}
|
|
|
|
function postText(string $key, int $maxLength): string
|
|
{
|
|
$value = $_POST[$key] ?? '';
|
|
$value = is_string($value) ? trim($value) : '';
|
|
if (mb_strlen($value) > $maxLength) throw new InvalidArgumentException('Eingabe zu lang.');
|
|
return $value;
|
|
}
|
|
|
|
function validateIsbnField(string $value, string $label): ?string
|
|
{
|
|
$value = trim($value);
|
|
if ($value === '') {
|
|
return null;
|
|
}
|
|
if (!preg_match('/^97[89](?:-[0-9]+)+$/', $value)) {
|
|
throw new InvalidArgumentException($label . ' muss als ISBN-13 mit Bindestrichen eingegeben werden, zum Beispiel 978-3-16-148410-0.');
|
|
}
|
|
|
|
$digits = str_replace('-', '', $value);
|
|
if (strlen($digits) !== 13) {
|
|
throw new InvalidArgumentException($label . ' muss genau 13 Ziffern enthalten.');
|
|
}
|
|
|
|
$sum = 0;
|
|
for ($index = 0; $index < 12; $index++) {
|
|
$sum += (int) $digits[$index] * ($index % 2 === 0 ? 1 : 3);
|
|
}
|
|
$checkDigit = (10 - ($sum % 10)) % 10;
|
|
if ((int) $digits[12] !== $checkDigit) {
|
|
throw new InvalidArgumentException($label . ' enthält eine ungültige Prüfziffer.');
|
|
}
|
|
|
|
return $value;
|
|
}
|
|
|
|
function redirectAdmin(): never
|
|
{
|
|
header('Location: admin.php');
|
|
exit;
|
|
}
|