Create csrf.php

This commit is contained in:
2026-07-19 14:56:26 +02:00
parent 1da4842847
commit 42faab17e8
+88
View File
@@ -0,0 +1,88 @@
<?php
/**
* CSRF-Schutz nach dem Synchronizer-Token-Pattern.
*
* Pro Session wird ein einziges, zufälliges Token erzeugt,
* das in jedem Formular als verstecktes Feld mitgeschickt und bei jeder
* zustandsändernden Anfrage serverseitig mit dem Session-Token verglichen
* wird.
*
* @author Niklas Ortmann
*/
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
/**
* Gibt das aktuelle CSRF-Token der Session zurück.
*
* Existiert noch kein Token, wird eines erzeugt und in der Session
* gespeichert.
*
* @return string Das CSRF-Token
*/
function csrf_token(): string
{
if (empty($_SESSION["csrf_token"]) || !is_string($_SESSION["csrf_token"])) {
$_SESSION["csrf_token"] = bin2hex(random_bytes(32));
}
return $_SESSION["csrf_token"];
}
/**
* Gibt ein verstecktes Formularfeld mit dem aktuellen CSRF-Token aus.
*
* Wird in jedem Formular benötigt, das eine zustandsändernde
* Aktion auslöst.
*
* @return void
*/
function csrf_field(): void
{
echo '<input type="hidden" name="csrf_token" value="'
. htmlspecialchars(csrf_token())
. '">';
}
/**
* Prüft, ob das per POST gesendete CSRF-Token zum Session-Token passt.
*
* Der Vergleich erfolgt zeitkonstant über hash_equals(), um
* Timing-Angriffe auf den Vergleich selbst auszuschließen.
*
* @return bool true, wenn das Token gültig ist
*/
function csrf_verify(): bool
{
$sentToken = $_POST["csrf_token"] ?? "";
$sessionToken = $_SESSION["csrf_token"] ?? "";
if (!is_string($sentToken) || $sentToken === "" || $sessionToken === "") {
return false;
}
return hash_equals($sessionToken, $sentToken);
}
/**
* Bricht die Anfrage ab und leitet mit einer Fehlermeldung um,
* wenn das mitgesendete CSRF-Token ungültig oder nicht vorhanden ist.
*
* Muss am Anfang jeder zustandsändernden POST-Aktion aufgerufen werden,
* bevor irgendeine Änderung an Daten vorgenommen wird.
*
* @param string $redirectTo Ziel-URL, zu der bei ungültigem Token
* weitergeleitet wird
* @return void
*/
function csrf_require_valid(string $redirectTo = "index.php"): void
{
if (!csrf_verify()) {
http_response_code(403);
$_SESSION["message"] = "invalid_csrf_token";
header("Location: " . $redirectTo);
exit();
}
}