diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..b022c19 --- /dev/null +++ b/SECURITY.md @@ -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. diff --git a/WEB/.env.production.example b/WEB/.env.production.example new file mode 100644 index 0000000..9be7e24 --- /dev/null +++ b/WEB/.env.production.example @@ -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}" diff --git a/WEB/app/Http/Middleware/SecurityHeaders.php b/WEB/app/Http/Middleware/SecurityHeaders.php new file mode 100644 index 0000000..af746f2 --- /dev/null +++ b/WEB/app/Http/Middleware/SecurityHeaders.php @@ -0,0 +1,68 @@ +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; + } +} diff --git a/WEB/bootstrap/app.php b/WEB/bootstrap/app.php index c3928c5..564336b 100644 --- a/WEB/bootstrap/app.php +++ b/WEB/bootstrap/app.php @@ -1,5 +1,6 @@ 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 { //