À l’heure où la grande majorité des sites web utilisent HTTPS, il n’est pas rare de constater que PHP sert encore ses cookies de session sans les attributs HttpOnly, Secure ou SameSite.
Pourtant, ces attributs sont essentiels pour durcir les cookies de session. Ils ne transforment pas une application vulnérable en coffre-fort suisse, mais ils réduisent plusieurs risques classiques : vol de cookie via JavaScript, transmission sur une connexion non chiffrée, ou envoi trop permissif dans certains contextes cross-site.
Heureusement, PHP permet de configurer ces directives directement depuis le fichier php.ini, depuis un pool PHP-FPM, depuis un fichier .user.ini, ou même depuis le code avec session_set_cookie_params().
À quoi servent HttpOnly, Secure et SameSite ?
Un cookie de session PHP ressemble souvent à ceci :
Set-Cookie: PHPSESSID=7d5h81tfiuna3p2p00o1v7b13q; path=/Code language: JavaScript (javascript)
Cette version fonctionne, mais elle reste trop permissive. On peut la renforcer avec plusieurs attributs :
HttpOnly empêche l’accès au cookie via JavaScript, notamment via document.cookie ;
Secure indique au navigateur de n’envoyer le cookie que via HTTPS ;
SameSite limite l’envoi du cookie dans les requêtes cross-site.
MDN recommande d’utiliser Secure pour que le cookie ne soit envoyé que sur HTTPS, et HttpOnly pour les cookies qui n’ont pas besoin d’être lus par JavaScript, notamment les identifiants de session.
Avec ces attributs, le cookie devient par exemple :
Set-Cookie: PHPSESSID=7d5h81tfiuna3p2p00o1v7b13q; path=/; secure; HttpOnly; SameSite=LaxCode language: JavaScript (javascript)
Activer les directives dans php.ini
La méthode la plus directe consiste à modifier le fichier php.ini utilisé par PHP-FPM.
Selon votre version de PHP, le chemin peut varier. Par exemple :
sudo nano /etc/php/8.3/fpm/php.ini
Ou, pour PHP 8.4 :
sudo nano /etc/php/8.4/fpm/php.ini
Recherchez ensuite les directives liées aux cookies de session :
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_only_cookies = 1
session.cookie_samesite = "Lax"Code language: JavaScript (javascript)
Ces valeurs indiquent à PHP de servir le cookie de session avec HttpOnly, Secure et SameSite=Lax. La directive session.use_only_cookies force PHP à utiliser uniquement les cookies pour transmettre l’identifiant de session, plutôt que de l’accepter dans l’URL.
La documentation PHP précise que session.cookie_secure limite les cookies de session aux connexions HTTPS, et que session.cookie_httponly ajoute l’attribut HttpOnly au cookie de session.
Redémarrez ensuite PHP-FPM :
sudo systemctl restart php8.3-fpmCode language: CSS (css)
Ou adaptez selon votre version :
sudo systemctl restart php8.4-fpmCode language: CSS (css)
Avec les anciennes distributions, on voyait souvent cette syntaxe :
sudo service php7.2-fpm restartCode language: CSS (css)
Elle fonctionne encore dans certains environnements, mais systemctl est généralement préférable sur les distributions Linux récentes.
Vérifier le fichier php.ini réellement utilisé
Avant de modifier un fichier au hasard, vérifiez toujours quel fichier php.ini est réellement chargé.
En ligne de commande :
php --ini
Attention toutefois : la CLI et PHP-FPM n’utilisent pas forcément le même fichier php.ini. Pour un site web servi par Nginx ou Apache via PHP-FPM, il faut modifier la configuration FPM, pas seulement la configuration CLI.
Vous pouvez aussi créer temporairement un fichier de test :
<?php
phpinfo();Code language: HTML, XML (xml)
Puis cherchez les valeurs suivantes dans la page :
Loaded Configuration File ;
session.cookie_httponly ;
session.cookie_secure ;
session.cookie_samesite ;
session.use_only_cookies.
Supprimez ce fichier dès que le test est terminé. Un phpinfo() laissé en ligne, c’est une invitation à fouiller vos entrailles serveur. Peu élégant.
Configuration via un pool PHP-FPM
Sur un serveur qui héberge plusieurs sites, il peut être plus propre de configurer ces valeurs au niveau du pool PHP-FPM du site concerné.
Par exemple :
sudo nano /etc/php/8.3/fpm/pool.d/www.conf
Ajoutez ou adaptez ces directives :
php_admin_value[session.cookie_httponly] = 1
php_admin_value[session.cookie_secure] = 1
php_admin_value[session.use_only_cookies] = 1
php_admin_value[session.cookie_samesite] = Lax
Puis testez la configuration et redémarrez PHP-FPM :
sudo php-fpm8.3 -t
sudo systemctl restart php8.3-fpmCode language: CSS (css)
L’avantage de cette méthode : elle évite d’appliquer une règle globale à toutes les applications PHP du serveur. Elle convient donc mieux aux environnements multi-sites ou aux hébergements avec plusieurs pools dédiés.
Configuration via .user.ini
Si vous n’avez pas accès au php.ini, certains hébergeurs permettent d’utiliser un fichier .user.ini à la racine du site.
Dans ce cas, créez ou modifiez le fichier :
nano .user.iniCode language: CSS (css)
Et ajoutez :
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_only_cookies = 1
session.cookie_samesite = "Lax"Code language: JavaScript (javascript)
Cette méthode dépend de la configuration de PHP-FPM et de l’hébergeur. Elle peut aussi mettre quelques minutes à être prise en compte, selon la valeur de user_ini.cache_ttl.
Définir les attributs depuis PHP
Si vous contrôlez le code de l’application, vous pouvez aussi définir les paramètres du cookie de session avant l’appel à session_start().
Depuis PHP 7.3, session_set_cookie_params() accepte un tableau d’options, dont secure, httponly et samesite.
<?php
session_set_cookie_params(
[
'lifetime' => 0,
'path' => '/',
'domain' => '',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]
);
session_start();Code language: HTML, XML (xml)
Cette méthode est pratique dans une application maison, car elle rend la configuration explicite dans le code. En revanche, elle doit impérativement être appelée avant session_start(), sinon elle arrive trop tard.
Pour une application existante, je préfère généralement configurer PHP-FPM ou php.ini, afin de durcir le comportement par défaut.
Quelle valeur choisir pour SameSite ?
La valeur SameSite mérite un peu d’attention.
SameSite=Lax convient à la majorité des sites classiques ;
SameSite=Strict est plus restrictif, mais peut casser certains parcours ;
SameSite=None autorise les contextes cross-site, mais exige Secure.
Pour un site web classique, un espace membre, un blog ou un back-office simple, SameSite=Lax est souvent le meilleur compromis.
SameSite=Strict peut empêcher l’envoi du cookie après un clic depuis un site externe. C’est parfois souhaitable, mais cela peut aussi surprendre les utilisateurs.
SameSite=None doit être réservé aux cas qui en ont réellement besoin : iframe cross-site, SSO, paiement embarqué, application découplée, intégration externe, ou cookie volontairement partagé dans un contexte tiers. Les navigateurs exigent alors aussi l’attribut Secure.
Tester les cookies avec curl
Après modification, on peut tester les en-têtes HTTP avec curl :
curl -I https:Code language: JavaScript (javascript)
Ou filtrer directement les cookies :
curl -sI https:Code language: JavaScript (javascript)
On cherche alors une ligne de ce type :
set-cookie: PHPSESSID=7d5h81tfiuna3p2p00o1v7b13q; path=/; secure; HttpOnly; SameSite=LaxCode language: JavaScript (javascript)
Si vous ne voyez aucun cookie PHPSESSID, ce n’est pas forcément un problème. Cela peut simplement vouloir dire que la page testée ne démarre pas de session PHP.
Dans ce cas, testez une page qui utilise réellement une session, comme une page de connexion, un panier, un espace membre, ou une application PHP qui appelle session_start().
Tester avec les outils développeur du navigateur
On peut aussi vérifier les cookies dans le navigateur.
- Ouvrez les outils développeur.
- Allez dans l’onglet Application ou Storage.
- Ouvrez la section Cookies.
- Sélectionnez le domaine du site.
- Vérifiez les colonnes
HttpOnly, Secure et SameSite.
C’est souvent plus lisible que curl, surtout lorsqu’un site définit plusieurs cookies à différents moments du parcours.
Attention aux sites derrière un reverse proxy ou Cloudflare
Si PHP se trouve derrière un reverse proxy, un load balancer ou Cloudflare, il faut vérifier que l’application détecte correctement HTTPS.
Sinon, certains scripts peuvent croire que la requête arrive en HTTP, même si le visiteur utilise bien HTTPS côté navigateur. Cela peut empêcher l’application de définir correctement les cookies sécurisés.
Dans une application PHP maison, on peut vérifier les en-têtes comme X-Forwarded-Proto, à condition de ne faire confiance qu’aux proxys contrôlés. Dans WordPress, la plupart des hébergeurs et configurations modernes gèrent déjà ce point, mais il reste à surveiller lors d’une migration.
Et pour WordPress ?
WordPress ne repose pas sur PHPSESSID pour son authentification principale. Il utilise ses propres cookies, notamment pour la connexion, l’administration et les commentaires.
Configurer session.cookie_httponly et session.cookie_secure durcit donc les sessions PHP classiques, mais ne modifie pas automatiquement tous les cookies créés par WordPress, WooCommerce ou les plugins.
En revanche, c’est utile si :
- un plugin démarre une session PHP ;
- une application PHP cohabite avec WordPress ;
- un espace membre utilise
session_start() ;
- un développement maison crée une session PHP ;
- un scanner de sécurité signale un
PHPSESSID sans attributs.
Pour les cookies propres à WordPress ou aux plugins, il faut vérifier chaque cookie dans les en-têtes HTTP. Certains attributs doivent être définis dans le code de l’application, d’autres peuvent être ajustés au niveau du serveur selon le contexte.
Ajouter des attributs aux cookies via Nginx
Dans l’article original, je mentionnais le module nginx_cookie_flag_module. Aujourd’hui, il vaut mieux éviter de s’appuyer sur ce vieux module abandonné si l’on peut faire autrement.
Nginx propose désormais la directive native proxy_cookie_flags pour ajouter ou modifier des attributs sur les cookies provenant d’un upstream. La documentation officielle montre par exemple l’ajout de httponly ou samesite=strict via cette directive.
Exemple avec un reverse proxy :
proxy_cookie_flags ~ secure httponly samesite=lax;
Ou pour cibler un cookie précis :
proxy_cookie_flags PHPSESSID secure httponly samesite=lax;
Attention : cette directive s’applique surtout aux cookies reçus depuis un upstream proxifié. Pour PHP-FPM en FastCGI classique, la meilleure solution reste de corriger la configuration PHP ou le code qui crée le cookie.
Et avec Apache ?
Avec Apache, on peut parfois ajuster les en-têtes Set-Cookie via mod_headers. Par exemple :
Header always edit Set-Cookie ^(.*)$ "$1; HttpOnly; Secure; SameSite=Lax"Code language: JavaScript (javascript)
Cette approche doit être utilisée avec prudence. Elle peut ajouter des attributs en double, modifier des cookies qui ne devraient pas l’être, ou casser certains cookies cross-site qui exigent SameSite=None; Secure.
Quand c’est possible, définissez les attributs au moment où le cookie est créé. Le serveur web peut dépanner, mais l’application reste l’endroit le plus propre pour exprimer l’intention.
Créer un cookie sécurisé avec setcookie()
Pour les cookies créés manuellement dans une application PHP, utilisez la syntaxe moderne de setcookie() avec un tableau d’options :
<?php
setcookie(
'example_cookie',
'example_value',
[
'expires' => time() + 3600,
'path' => '/',
'domain' => '',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]
);Code language: HTML, XML (xml)
Cette syntaxe est plus lisible que l’ancienne suite d’arguments positionnels. Elle évite aussi les erreurs bêtes de type “j’ai mis true au mauvais endroit”, grand classique du PHP vintage.
Cas particulier : SameSite=None
Si un cookie doit fonctionner dans un contexte tiers, par exemple dans une iframe, une intégration externe, un SSO ou certains flux de paiement, vous aurez peut-être besoin de SameSite=None.
Dans ce cas, il faut impérativement ajouter Secure :
session.cookie_secure = 1
session.cookie_samesite = "None"Code language: JavaScript (javascript)
Sans Secure, les navigateurs modernes peuvent refuser le cookie. MDN précise que les cookies avec SameSite=None doivent aussi spécifier l’attribut Secure.
Ne choisissez donc pas SameSite=None par habitude. Utilisez-le uniquement si votre application en a réellement besoin.
Mémo rapide
sudo nano /etc/php/8.3/fpm/php.ini
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_only_cookies = 1
session.cookie_samesite = "Lax"
sudo php-fpm8.3 -t
sudo systemctl restart php8.3-fpm
curl -sI https:Code language: PHP (php)
Conclusion
Pour sécuriser les cookies de session PHP sur un site HTTPS, la base moderne consiste à activer ces directives :
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_only_cookies = 1
session.cookie_samesite = "Lax"Code language: JavaScript (javascript)
HttpOnly limite l’accès JavaScript au cookie, Secure empêche son envoi en HTTP, et SameSite=Lax réduit l’exposition aux requêtes cross-site sans casser la majorité des parcours classiques.
Pour une application PHP maison, vous pouvez aussi définir ces options directement avec session_set_cookie_params() avant session_start(). Pour WordPress, cette configuration durcit surtout les sessions PHP classiques, mais elle ne remplace pas un audit des cookies créés par le cœur, le thème et les plugins.
Enfin, vérifiez toujours le résultat avec curl ou les outils développeur du navigateur. En matière de cookies, la configuration déclarée compte moins que l’en-tête réellement envoyé. Le navigateur, lui, ne lit pas vos intentions. Fâcheux, mais juste.