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:
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user