Added CSRF protection

This commit is contained in:
julien
2026-03-09 17:15:14 +01:00
parent 96214378d6
commit 99a1f2c5ab
4 changed files with 71 additions and 2 deletions

View File

@@ -8,16 +8,24 @@ use Dotenv\Dotenv;
use Slim\Factory\AppFactory; use Slim\Factory\AppFactory;
use Slim\Views\TwigMiddleware; use Slim\Views\TwigMiddleware;
use Slim\Views\Twig; use Slim\Views\Twig;
use Slim\Csrf\Guard;
use Medoo\Medoo; use Medoo\Medoo;
use App\Controllers\PostController; use App\Controllers\PostController;
use App\Repositories\PostRepository; use App\Repositories\PostRepository;
use App\Services\HtmlSanitizer; use App\Services\HtmlSanitizer;
use App\Services\HtmlPurifierFactory; use App\Services\HtmlPurifierFactory;
use App\Services\CsrfExtension;
use App\Database\Migrator; use App\Database\Migrator;
use App\Bootstrap; use App\Bootstrap;
use App\Routes; use App\Routes;
use App\Config; use App\Config;
// ============================================
// Démarrer la session PHP
// ============================================
session_start();
// ============================================ // ============================================
// Vérifier les répertoires // Vérifier les répertoires
// ============================================ // ============================================
@@ -38,16 +46,29 @@ $dotenv->load();
$env = $_ENV['APP_ENV'] ?? 'production'; $env = $_ENV['APP_ENV'] ?? 'production';
$isDev = strtolower($env) === 'development'; $isDev = strtolower($env) === 'development';
// ============================================
// Initialisation de l'application Slim
// ============================================
$app = AppFactory::create();
$responseFactory = $app->getResponseFactory();
// ============================================ // ============================================
// Initialisation des services // Initialisation des services
// ============================================ // ============================================
// CSRF Guard (middleware)
$csrf = new Guard($responseFactory);
// Twig // Twig
$twig = Twig::create( $twig = Twig::create(
__DIR__ . '/../views', __DIR__ . '/../views',
['cache' => Config::getTwigCache($isDev)] ['cache' => Config::getTwigCache($isDev)]
); );
// Ajouter l'extension CSRF à Twig
$twig->addExtension(new CsrfExtension($csrf));
// Medoo (SQLite) // Medoo (SQLite)
$dbFile = Config::getDatabasePath(); $dbFile = Config::getDatabasePath();
$db = new Medoo([ $db = new Medoo([
@@ -69,13 +90,15 @@ $htmlSanitizer = new HtmlSanitizer($htmlPurifier);
$postRepository = new PostRepository($db); $postRepository = new PostRepository($db);
// ============================================ // ============================================
// Slim App // Middleware
// ============================================ // ============================================
$app = AppFactory::create();
$app->addBodyParsingMiddleware(); $app->addBodyParsingMiddleware();
$app->add(TwigMiddleware::create($app, $twig)); $app->add(TwigMiddleware::create($app, $twig));
// Enregistrer le middleware CSRF pour toutes les routes
$app->add($csrf);
// ============================================ // ============================================
// Routes // Routes
// ============================================ // ============================================

View File

@@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
namespace App\Services;
use Slim\Csrf\Guard;
use Twig\Extension\AbstractExtension;
use Twig\Extension\GlobalsInterface;
/**
* Extension Twig pour accéder aux tokens CSRF dans les templates.
*/
final class CsrfExtension extends AbstractExtension implements GlobalsInterface
{
public function __construct(private Guard $csrf)
{
}
public function getGlobals(): array
{
$csrfNameKey = $this->csrf->getTokenNameKey();
$csrfValueKey = $this->csrf->getTokenValueKey();
$csrfName = $this->csrf->getTokenName();
$csrfValue = $this->csrf->getTokenValue();
return [
'csrf' => [
'keys' => [
'name' => $csrfNameKey,
'value' => $csrfValueKey,
],
'name' => $csrfName,
'value' => $csrfValue,
],
];
}
}

View File

@@ -32,6 +32,10 @@
<a href="/admin/edit/{{ post.id }}" class="btn btn-sm btn-secondary">Éditer</a> <a href="/admin/edit/{{ post.id }}" class="btn btn-sm btn-secondary">Éditer</a>
<form method="post" action="/admin/delete/{{ post.id }}" style="display:inline;"> <form method="post" action="/admin/delete/{{ post.id }}" style="display:inline;">
{# Tokens CSRF #}
<input type="hidden" name="{{ csrf.keys.name }}" value="{{ csrf.name }}">
<input type="hidden" name="{{ csrf.keys.value }}" value="{{ csrf.value }}">
<button type="submit" class="btn btn-sm btn-danger" <button type="submit" class="btn btn-sm btn-danger"
onclick="return confirm('Supprimer cet article ?')"> onclick="return confirm('Supprimer cet article ?')">
Supprimer Supprimer

View File

@@ -20,6 +20,10 @@ Créer un article
{# Formulaire identifié pour le script JavaScript #} {# Formulaire identifié pour le script JavaScript #}
<form id="articleForm" method="post" action="{{ action }}"> <form id="articleForm" method="post" action="{{ action }}">
{# Tokens CSRF #}
<input type="hidden" name="{{ csrf.keys.name }}" value="{{ csrf.name }}">
<input type="hidden" name="{{ csrf.keys.value }}" value="{{ csrf.value }}">
<p> <p>
<label for="title">Titre<br> <label for="title">Titre<br>
<input type="text" id="title" name="title" value="{{ post.title|default('') }}" required maxlength="255"> <input type="text" id="title" name="title" value="{{ post.title|default('') }}" required maxlength="255">