corentin_wakdo/src/app/Auth/Csrf.php
Corentin JOGUET 1b0b20c12d
All checks were successful
CI / secret-scan (push) Successful in 7s
CI / php-lint (push) Successful in 17s
CI / static-tests (push) Successful in 32s
CI / auto-merge (push) Has been skipped
feat: authentification back-office P2 (login/logout/reset, throttle, audit) (#11)
2026-06-15 20:18:59 +02:00

60 lines
1.7 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Auth;
/**
* Jeton CSRF synchroniseur stocke en session (RG-T01). Choisi plutot que le
* double-submit (plus faible derriere un domaine parent partage) ou un HMAC
* stateless (inutile puisqu'on a deja un etat serveur en session).
*
* Comparaison en temps constant (hash_equals) ; le jeton est re-genere apres
* session_regenerate_id pour qu'un jeton plante avant l'authentification ne
* puisse pas etre rejoue.
*/
final class Csrf
{
private const KEY = '_csrf';
/**
* Jeton stable de la session : genere une fois (32 octets CSPRNG en hex)
* puis reutilise tant que la session vit.
*/
public static function token(SessionManager $session): string
{
$existing = $session->get(self::KEY);
if (is_string($existing) && $existing !== '') {
return $existing;
}
return self::rotate($session);
}
/**
* Vrai uniquement si un jeton existe en session et egale (temps constant) le
* jeton soumis. Toute absence (pas de jeton, soumission vide) renvoie false.
*/
public static function validate(SessionManager $session, ?string $submitted): bool
{
$stored = $session->get(self::KEY);
if (!is_string($stored) || $stored === '' || $submitted === null || $submitted === '') {
return false;
}
return hash_equals($stored, $submitted);
}
/**
* Re-genere le jeton (apres regeneration d'ID de session sur login reussi) :
* invalide tout jeton anterieur a l'authentification.
*/
public static function rotate(SessionManager $session): string
{
$token = bin2hex(random_bytes(32));
$session->set(self::KEY, $token);
return $token;
}
}