{{ user.name }}
{# Désactiver l'échappement UNIQUEMENT si nécessaire #}{{ user.bio|raw }}
{# Utiliser des filtres appropriés #}{{ user.description|striptags }}
``` ### Configuration des Content Security Policy Headers : ```php // src/EventListener/SecurityHeadersListener.php namespace App\EventListener; use Symfony\Component\EventDispatcher\Attribute\AsEventListener; use Symfony\Component\HttpKernel\Event\ResponseEvent; use Symfony\Component\HttpKernel\KernelEvents; #[AsEventListener(event: KernelEvents::RESPONSE)] class SecurityHeadersListener { public function onKernelResponse(ResponseEvent $event): void { $resp>getResponse(); // Content Security Policy $response->headers->set( 'Content-Security-Policy', "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline';" ); // Headers de sécurité supplémentaires $response->headers->set('X-Content-Type-Options', 'nosniff'); $response->headers->set('X-Frame-Options', 'SAMEORIGIN'); $response->headers->set('X-XSS-Protection', '1; mode=block'); $response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); $response->headers->set('Referrer-Policy', 'strict-origin-when-cross-origin'); } } ``` ## 5. Validation et Sanitisation des données ### Utilisation des contraintes de validation : ```php use Symfony\Component\Validator\Constraints as Assert; class User { #[Assert\NotBlank(message: 'L\'email ne peut pas être vide')] #[Assert\Email(message: 'L\'email {{ value }} n\'est pas valide')] #[Assert\Length(max: 180)] private ?string $email = null; #[Assert\NotBlank] #[Assert\Length( min: 8, max: 4096, minMessage: 'Le mot de passe doit contenir au moins {{ limit }} caractères' )] #[Assert\Regex( pattern: '/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/', message: 'Le mot de passe doit contenir au moins une majuscule, une minuscule et un chiffre' )] private ?string $password = null; #[Assert\NotBlank] #[Assert\Length(min: 2, max: 100)] #[Assert\Regex( pattern: '/^[a-zA-Z\s]+$/', message: 'Le nom ne peut contenir que des lettres' )] private ?string $name = null; } ``` ## 6. Gestion sécurisée des fichiers uploadés ```php use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Component\Validator\Constraints as Assert; class DocumentUploadController extends AbstractController { #[Route('/upload', name: 'app_upload')] public function upload(Request $request): Response { $uploadedFile = $request->files->get('document'); // Validation du fichier $violati>validator->validate($uploadedFile, [ new Assert\File([ 'maxSize' => '5M', 'mimeTypes' => [ 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', ], 'mimeTypesMessage' => 'Seuls les fichiers PDF et Word sont acceptés', ]) ]); if (count($violations) > 0) { throw new \Exception('Fichier invalide'); } // Génération d'un nom de fichier sécurisé $originalFilename = pathinfo($uploadedFile->getClientOriginalName(), PATHINFO_FILENAME); $safeFilename = transliterator_transliterate( 'Any-Latin; Latin-ASCII; [^A-Za-z0-9_] remove; Lower()', $originalFilename ); $newFilename = $safeFilename . '-' . uniqid() . '.' . $uploadedFile->guessExtension(); // Déplacement du fichier $uploadedFile->move( $this->getParameter('upload_directory'), $newFilename ); return new JsonResponse(['filename' => $newFilename]); } } ``` ## 7. Protection contre les attaques de limitation de débit (Rate Limiting) ### Installation du composant : ```bash composer require symfony/rate-limiter ``` ### Configuration (config/packages/rate_limiter.yaml) : ```yaml framework: rate_limiter: login: policy: 'sliding_window' limit: 5 interval: '15 minutes' api: policy: 'token_bucket' limit: 100 rate: { interval: '1 hour', amount: 100 } ``` ### Implémentation : ```php use Symfony\Component\RateLimiter\RateLimiterFactory; class LoginController extends AbstractController { #[Route('/login', name: 'app_login')] public function login( Request $request, RateLimiterFactory $loginLimiter ): Response { // Création d'un limiteur basé sur l'IP $limiter = $loginLimiter->create($request->getClientIp()); // Vérification de la limite if (false === $limiter->consume(1)->isAccepted()) { throw new TooManyRequestsHttpException( 'Trop de tentatives de connexion. Veuillez réessayer dans 15 minutes.' ); } // Logique de connexion... } } ``` ## 8. Sécurisation des variables d'environnement ### Utilisation de secrets chiffrés : ```bash # Générer une clé de chiffrement php bin/console secrets:generate-keys # Définir un secret php bin/console secrets:set DATABASE_PASSWORD # Lister les secrets php bin/console secrets:list ``` ### Fichier .env pour le développement uniquement : ```env # .env (non commité en production) APP_ENV=dev APP_SECRET=changeme # .env.local (ignoré par Git) DATABASE_URL="postgresql://user:password@127.0.0.1:5432/dbname" ``` ## 9. Audit de sécurité et dépendances ### Vérification des vulnérabilités : ```bash # Vérifier les dépendances vulnérables composer audit # Avec Symfony CLI symfony security:check # Mise à jour des dépendances composer update --with-all-dependencies ``` ## 10. Logging et Monitoring de sécurité ```php use Psr\Log\LoggerInterface; class SecurityEventSubscriber implements EventSubscriberInterface { public function __construct( private LoggerInterface $securityLogger ) {} public static function getSubscribedEvents(): array { return [ AuthenticationSuccessEvent::class => 'onAuthenticationSuccess', AuthenticationFailureEvent::class => 'onAuthenticationFailure', LoginFailureEvent::class => 'onLoginFailure', ]; } public function onAuthenticationSuccess(AuthenticationSuccessEvent $event): void { $this->securityLogger->info('Connexion réussie', [ 'user' => $event->getAuthenticationToken()->getUserIdentifier(), 'ip' => $event->getRequest()?->getClientIp(), 'user_agent' => $event->getRequest()?->headers->get('User-Agent'), ]); } public function onAuthenticationFailure(AuthenticationFailureEvent $event): void { $this->securityLogger->warning('Échec d\'authentification', [ 'exception' => $event->getAuthenticationException()->getMessage(), 'ip' => $event->getRequest()?->getClientIp(), ]); } } ``` ## Checklist de sécurité Symfony - [ ] Activer HTTPS en production (HSTS) - [ ] Configurer les headers de sécurité (CSP, X-Frame-Options, etc.) - [ ] Utiliser des paramètres préparés pour toutes les requêtes SQL - [ ] Activer la protection CSRF sur tous les formulaires - [ ] Hacher les mots de passe avec des algorithmes modernes (bcrypt, argon2) - [ ] Valider et sanitiser toutes les entrées utilisateur - [ ] Implémenter le rate limiting sur les endpoints sensibles - [ ] Sécuriser les uploads de fichiers (validation MIME type, taille) - [ ] Utiliser les secrets Symfony pour les données sensibles - [ ] Configurer correctement les cookies (Secure, HttpOnly, SameSite) - [ ] Mettre en place des logs de sécurité - [ ] Effectuer des audits réguliers des dépendances - [ ] Implémenter l'authentification à deux facteurs pour les comptes sensibles - [ ] Définir des politiques de contrôle d'accès strictes - [ ] Configurer les CORS correctement si API ## Conclusion La sécurité d'une application Symfony nécessite une approche multi-couches. Bien que le framework offre d'excellents outils natifs, leur configuration correcte et leur utilisation appropriée restent la responsabilité du développeur. En suivant ces bonnes pratiques et en restant informé des dernières vulnérabilités (OWASP Top 10, CVE), vous pouvez considérablement renforcer la posture de sécurité de vos applications. N'oubliez pas : la sécurité n'est pas un état final mais un processus continu d'amélioration et de vigilance. ## Ressources supplémentaires - [Documentation officielle Symfony Security](https://symfony.com/doc/current/security.html) - [OWASP Top 10](https://owasp.org/www-project-top-ten/) - [Symfony Security Advisories](https://symfony.com/security) - [Guide de sécurité PHP](https://www.php.net/manual/fr/security.php)