feat(securite): en-tetes HTTP, trust proxies et configuration de production durcie

- middleware SecurityHeaders (HSTS, CSP, X-Frame-Options, etc.)
- trustProxies pour le reverse proxy Nginx Proxy Manager
- .env.production.example (APP_DEBUG=false, sessions chiffrees, cookies Secure)
- politique de securite SECURITY.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-08 18:43:17 +02:00
parent d75afe0aac
commit ebe1836f7e
4 changed files with 172 additions and 1 deletions
+41
View File
@@ -0,0 +1,41 @@
# Politique de sécurité — CESIZen
## Versions supportées
| Version | Support sécurité |
|---------|------------------|
| 1.x | ✅ Oui |
| < 1.0 | ❌ Non |
## Signaler une vulnérabilité
Toute faille de sécurité doit être signalée **de manière privée**, sans
divulgation publique préalable :
1. Ouvrir un ticket **confidentiel** sur le dépôt Gitea, ou
2. Contacter l'équipe à `security@cesizen.fr`.
Merci d'inclure : description, impact, étapes de reproduction et, si possible,
une proposition de correctif.
**Délais d'engagement :**
- Accusé de réception : sous 48 h
- Évaluation de la gravité (CVSS) : sous 5 jours ouvrés
- Correctif pour les failles critiques : sous 15 jours
## Mesures de sécurité en place
- Chiffrement des échanges via **HTTPS/TLS** (Let's Encrypt via Nginx Proxy Manager)
- Hachage des mots de passe avec **bcrypt** (12 tours)
- Authentification API par jetons **Laravel Sanctum**
- En-têtes de sécurité HTTP (HSTS, CSP, X-Frame-Options…)
- Protection **CSRF** native Laravel sur les formulaires
- Requêtes préparées (Eloquent/PDO) contre les **injections SQL**
- Audit automatisé des dépendances (`composer audit`, `npm audit`) dans la CI
- Chiffrement des données de session et cookies `Secure` / `HttpOnly`
## Conformité RGPD
Les données à caractère personnel sont traitées conformément au RGPD :
minimisation, droit à l'effacement (suppression de compte), consentement,
et anonymisation des données statistiques.
+55
View File
@@ -0,0 +1,55 @@
# ══════════════════════════════════════════════════════════════
# CESIZen — Modèle de configuration PRODUCTION
# Copier en `.env` sur le serveur puis renseigner les valeurs.
# ⚠️ Ne JAMAIS committer le .env réel (secrets, clés, mots de passe).
# ══════════════════════════════════════════════════════════════
APP_NAME=CESIZen
APP_ENV=production
APP_KEY= # généré via `php artisan key:generate`
APP_DEBUG=false # JAMAIS true en production (fuite d'infos)
APP_URL=https://cesizen.sam-coffre.duckdns.org
APP_LOCALE=fr
APP_FALLBACK_LOCALE=fr
APP_FAKER_LOCALE=fr_FR
# ── Journalisation ────────────────────────────────────────────
LOG_CHANNEL=stack
LOG_STACK=daily # rotation quotidienne des logs
LOG_LEVEL=warning # on ne loggue pas le debug en prod
# ── Base de données (MySQL/MariaDB en production) ─────────────
DB_CONNECTION=mysql
DB_HOST=cesizen-db
DB_PORT=3306
DB_DATABASE=cesizen
DB_USERNAME=cesizen_app # compte applicatif à privilèges limités
DB_PASSWORD= # mot de passe fort (gestionnaire de secrets)
# ── Sessions & cookies (durcissement) ─────────────────────────
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=true # chiffrement des données de session
SESSION_SECURE_COOKIE=true # cookie envoyé uniquement en HTTPS
SESSION_HTTP_ONLY=true # cookie inaccessible au JavaScript (anti-XSS)
SESSION_SAME_SITE=lax # protection CSRF renforcée
# ── Sécurité mots de passe ────────────────────────────────────
BCRYPT_ROUNDS=12 # coût du hachage bcrypt
# ── Cache / File d'attente ────────────────────────────────────
CACHE_STORE=database
QUEUE_CONNECTION=database
FILESYSTEM_DISK=local
# ── Mail (notifications, réinitialisation de mot de passe) ────
MAIL_MAILER=smtp
MAIL_HOST=
MAIL_PORT=587
MAIL_USERNAME=
MAIL_PASSWORD=
MAIL_FROM_ADDRESS="no-reply@cesizen.fr"
MAIL_FROM_NAME="${APP_NAME}"
VITE_APP_NAME="${APP_NAME}"
@@ -0,0 +1,68 @@
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
/**
* SecurityHeaders
* ---------------
* Ajoute les en-têtes HTTP de sécurité recommandés (OWASP Secure Headers)
* à toutes les réponses de l'application.
*
* Ces en-têtes renforcent la protection contre :
* - le clickjacking (X-Frame-Options)
* - le MIME-sniffing (X-Content-Type-Options)
* - les fuites de référent (Referrer-Policy)
* - le vol de session sur HTTP (Strict-Transport-Security / HSTS)
* - certaines injections de contenu (Content-Security-Policy)
*/
class SecurityHeaders
{
public function handle(Request $request, Closure $next): Response
{
/** @var Response $response */
$response = $next($request);
// Empêche l'affichage du site dans une iframe (anti-clickjacking)
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
// Empêche le navigateur de "deviner" le type MIME d'un fichier
$response->headers->set('X-Content-Type-Options', 'nosniff');
// Limite les informations de référent envoyées vers l'extérieur
$response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
// Désactive des API navigateur sensibles non utilisées par l'app
$response->headers->set(
'Permissions-Policy',
'geolocation=(), microphone=(), camera=(), payment=()'
);
// HSTS : force le navigateur à n'utiliser que HTTPS pendant 1 an.
// Uniquement sur connexion sécurisée (le reverse proxy NPM termine le TLS).
if ($request->isSecure()) {
$response->headers->set(
'Strict-Transport-Security',
'max-age=31536000; includeSubDomains'
);
}
// Content-Security-Policy : restreint les sources de contenu autorisées.
// 'unsafe-inline' est toléré pour les styles/scripts Blade + Filament.
$response->headers->set(
'Content-Security-Policy',
"default-src 'self'; "
."img-src 'self' data: https:; "
."script-src 'self' 'unsafe-inline' 'unsafe-eval'; "
."style-src 'self' 'unsafe-inline' https:; "
."font-src 'self' data: https:; "
."connect-src 'self'; "
."frame-ancestors 'self'"
);
return $response;
}
}
+8 -1
View File
@@ -1,5 +1,6 @@
<?php
use App\Http\Middleware\SecurityHeaders;
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
@@ -12,7 +13,13 @@ return Application::configure(basePath: dirname(__DIR__))
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
//
// L'application est servie derrière un reverse proxy (Nginx Proxy Manager)
// qui termine le TLS : on lui fait confiance pour détecter HTTPS,
// l'IP réelle du client et le bon schéma d'URL.
$middleware->trustProxies(at: '*');
// En-têtes de sécurité HTTP appliqués à toutes les réponses.
$middleware->append(SecurityHeaders::class);
})
->withExceptions(function (Exceptions $exceptions): void {
//