Un projet très intéressant est sorti il y a peu nommé Mozilla Observatory. Cet outil permet de tester divers points concernant la sécurité des sites Web.
En grand joueur, je me suis amusé à essayer d’obtenir la meilleure note possible, qui selon le barème est de 135/100. Je suis arrivé à 130/100 sur le site de Röcssti, ce qui n’est déjà pas si mal.
Mise à jour du 27 Octobre 2016 : Röcssti étant désormais sur HSTS preload list, ce n’est plus 120/100 mais 125/100 au score.
Mise à jour du 22 Novembre 2016 : Röcssti envoyant un en-tête Referrer-Policy
, ce n’est plus 125/100 mais 130/100 au score.
Comme cela a l’air d’intéresser pas mal de monde sur Twitter, je vais essayer d’expliquer au mieux ce qu’il faut faire pour faire péter le A+, et essayer de vulgariser tous ces noms barbares. :)
Autant le dire de suite : certains points peuvent être difficiles à obtenir selon vos conditions. Je ne suis pas non plus un expert sur chacun des points, vous m’excuserez d’avance le langage de béotien. Si un expert ou une experte en sécurité veut bien vulgariser ceux que je ne maitrise pas, il ou elle est le ou la bienvenu(e) !
Attention, bon nombre des choses que je présente ici vous font entrer dans le merveilleux monde du HTTPS, mais il sera très difficile voir quasi-impossible d’en sortir si vous déployez tout cela. Réfléchissez bien avant de déployer toute cette armada, car certains points ne s’annuleront vraiment pas facilement.
HTTPS/TLS
Le premier point sans lequel vous n’irez pas très loin est HTTPS. Curieusement, on croit que la sécurité vient du type de certificat (cela joue), mais en fait c’est aussi et surtout la configuration serveur qui va vous octroyer une bonne ou mauvaise note. Si le serveur est configuré pour utiliser des protocoles dépassés (et donc vulnérables), vos notes risquent de vite baisser. Par contre, si votre serveur ne supporte que les protocoles les plus récents, de vieux navigateurs pourront être exclus : si un agent utilisateur n’arrive pas à se mettre d’accord sur un protocole avec le serveur, fin de la discussion. C’est très bien expliqué dans « Le réveil de la crypto forte ». C’est un équilibre à trouver.
Si vous avez la maitrise de votre serveur, vous pouvez vous lancer à étudier cela. Personnellement, j’admets bien volontiers que la config serveur n’est pas mon fort. La documentation donnée par Mozilla est intéressante : Recommended Configurations for TLS. Je reconnais que ce point est délicat si on n’a pas de connaissances sur le sujet.
Si vous n’avez pas la main sur votre serveur, c’est là que votre hébergeur doit prendre le relai… et une initiative est tombée à point nommé : Let’s Encrypt. Cela vous évite déjà le fait de payer pour votre certificat, et l’outil est réputé plutôt simple. De ce côté-là, Röcssti étant hébergé chez Infomaniak (partenaire de Let’s Encrypt depuis un certain temps), cela a été très facile à gérer. Un simple clic dans l’admin permet d’installer un certificat, et ce dernier se renouvelle automatiquement.
Ajoutons à cela qu’en début d’année, je leur ai gentiment demandé d’améliorer leurs configurations… et ils l’ont fait !
Bref, n’hésitez pas à challenger vos hébergeurs, et à les féliciter quand ils jouent le jeu. Et n’oubliez pas que ce qui est fait pour vous bénéficie à tout le monde.
Pour tester votre configuration :
- SSL Server Test (chez SSL labs)
- Cryptcheck (plus sévère)
HTTP Strict Transport Security (HSTS) et redirections
Maintenant que votre site peut être servi en HTTPS, il va falloir… ne plus rien utiliser d’autre. Pour cela, vous pouvez commencer par mettre en place des redirections (via htaccess ou en-tête PHP). La logique de Mozilla Observatory est la suivante : le nom de domaine doit d’abord être redirigé vers le même en HTTPS (ce qui permettra d’utiliser HSTS correctement), et quoi qu’il arrive la destination finale doit être en HTTPS.
Là, rien de bien particulier. Cela peut se faire via htaccess par exemple :
RewriteEngine On
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
La redirection renvoie vers la version sécurisée avec le rewrite flag permanent redirect, toutefois, cela est insuffisant. C’est là qu’entre en scène HTTP Strict Transport Security (HSTS). Selon Wikipédia, « HSTS est un mécanisme de politique de sécurité proposé pour HTTP », qui intime à l’agent utilisateur de remplacer automatiquement tous les liens non sécurisés par des liens sécurisés (on n’attend même plus la redirection côté serveur pour aller directement vers la version sécurisée). Voici l’en-tête en question chez moi :
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Grosso modo, dès que l’agent utilisateur reçoit cet en-tête : il ne va plus utiliser autre chose qu’HTTPS durant 1 an (31536000s = 1 an dans mon cas) et ce même si on utilise une URL non sécurisée (elle sera automatiquement « upgradée » vers HTTPS), les sous-domaines auront également le même traitement.
C’est pour cela que par exemple vous aurez l’impression chez Infomaniak que dès que vous utilisez une URL en HTTPS, HTTPS sera activé automatiquement pour tout le domaine. C’est le cas sur votre machine (qui a reçu l’en-tête HSTS), mais pas nécessairement partout si vous n’avez pas activé les redirections. Par contre, n’importe qui qui reçoit cet en-tête de votre site sera systématiquement renvoyé vers HTTPS.
La seule limite de cet en-tête surpuissant est la première connexion : tant qu’on ne l’a pas reçu, on n’est pas protégé. C’est là qu’un autre mécanisme entre en jeu pour compléter cela : on peut inscrire son nom de domaine sur une liste de pré-chargement HSTS preload list pour demander son inclusion dans cette liste. Cette liste est utilisée par les navigateurs modernes. En clair, si votre site est dans cette liste, les navigateurs utiliseront HTTPS de suite et quoi qu’il arrive.
Le preload
dans l’en-tête au-dessus indique qu’on donne le consentement pour y être inclus. Pour Röcssti, j’ai déjà fait la demande, mais c’est toujours en attente, et d’après ce que j’ai lu, c’est plutôt long pour y être ajouté, et aussi long pour en être retiré. Un point de détail, ne mettez pas de point-virgule après preload
, sinon HSTS preload list vous gueulera dessus. J’ai eu le problème avec l’en-tête mis par défaut chez Infomaniak, vous pouvez le surcharger en attendant que cela soit fixé (le bug a été remonté et a été fixé).
Mise à jour du 27 Octobre 2016 : Röcssti est désormais sur HSTS preload list, hop, 5 points de plus !
Je le redis, attention, bon nombre des choses énoncées ici vous font entrer dans le merveilleux monde du HTTPS, mais il sera très difficile voir quasi-impossible d’en sortir. Si vous avez le moindre doute qu’un jour vous deviez revenir à du HTTPS sans le « S », réfléchissez bien.
HTTP Public Key Pinning (HPKP)
Là on touche du doigt un point délicat à gérer si vous n’avez pas la main mise sur votre serveur et/ou si vous n’avez de bonnes compétences en la matière.
Grosso modo, l’idée de l’en-tête HPKP est de faire présenter une liste à la première connexion réussie (sur le principe joliment nommé TOFU, tout comme HSTS) représentant les clés de confiance par le serveur (via un hashage cryptographique, un SHA-256
). Le navigateur mémorise cette liste. Aux prochaines connexions, les navigateurs ayant gardé en mémoire la liste des clés de confiance vont re-vérifier les clés publiques, et, si le certificat a été compromis, le navigateur refusera la connexion.
Voici des ressources sur le sujet :
- HTTPS Public Key Pinning, chez Stéphane Bortzmeyer
- HPKP: HTTPS Public Key Pinning, by Scott Helme
- Report-uri tools, by Scott Helme
Je le redis (et les ressources ci-dessus aussi) : faites vraiment gaffe avant de vous lancer là-dedans, procédez doucement en mode Public-Key-Pins-Report-Only
, car il est très facile de se blackbouler soi-même si on ne fait pas attention. N’utilisez pas bêtement l’outil ci-dessus, ne foncez pas tête baissée, il est vraiment facile de se planter si on ne lit pas la documentation (j’insiste, mais vous ne pourrez pas dire que vous n’avez pas été prévenus).
Pour ma part, je n’ai pas encore tous les tenants et les aboutissants de cette techno sur mon hébergement, donc je préfère attendre et apprendre avant de m’y frotter. Peut-être la solution viendra de l’hébergeur directement, cela sera à voir.
Referrer-Policy
Comme son nom l’indique, Referrer-Policy
indique votre politique en matière de Referrer
. Essayons d’expliquer cela simplement.
Typiquement, si vous chargez une image http://www.foo.com/image.jpg
sur un site depuis http://www.monsite.com/plop.htm
, votre navigateur va envoyer une requête en indiquant dans les en-têtes que l’origine de la requête est Referer: http://www.monsite.com/plop.htm
. C’est ce que vous voyez dans certains outils de logs qui vous disent par exemple que votre fichier a été affiché depuis « telle adresse ». Le principe est équivalent pour des liens, ce qui vous permet de savoir que « telle adresse » a un lien vers une page du vôtre (par exemple sous Google Analytics).
Le seul souci de cette valeur, c’est qu’elle peut transmettre à votre insu des informations sensibles. C’est là que cet en-tête entre en jeu, il permet de définir explicitement votre politique à ce propos. Voici les valeurs recommandées par Mozilla Observatory :
no-referrer
: on n’envoie jamais l’en-têtesame-origin
: on n’envoie l’en-tête que sur les requêtes ayant la même originestrict-origin
: on envoie l’en-tête à toutes les origines, mais seulement l’URL sans le chemin (on sait que cela vient de « tel site » mais sans en savoir plus)strict-origin-when-cross-origin
: on envoie l’en-tête complète sur la même origine, et seulement l’URL sans le chemin sur les autres origines
Pour ma part, j’ai utilisé la dernière valeur.
SubResource Integrity (SRI)
Là je vais être fainéant, je viens d’écrire un billet sur le sujet : À propos de SubResource integrity (SRI) et de traduire en français Subresource Integrity sur le MDN.
Grosso modo, le cas classique d’utilisation est la vérification de l’intégrité d’un fichier qu’on télécharge par exemple depuis un CDN. Dans mon cas, ce n’était pas obligatoire, néanmoins je me suis amusé à l’ajouter pour mon fichier JavaScript.
<script src="./layout/js/jquery-mini_1460217696.js"
async="async"
integrity="sha384-pQLaaAifGwNK5mu6Y9T+zVs8G05K8aE3JBHEIzwnzhmJ5QR1mNwR8/QtV4lRkrm2"></script>
Si le hash ne correspond plus (autrement dit si le fichier a été compromis), patatrac, le navigateur n’exécutera plus ce fichier JavaScript.
Cross-origin Resource Sharing (CORS)
CORS est un en-tête HTTP (encore un !) indiquant votre politique concernant les sites souhaitant accéder à vos ressources depuis un autre nom de domaine, via des méthodes comme XMLHttpRequest.
Pour vous donner un exemple, supposons que je mette en place un CDN proposant les versions minifiées de Röcssti. Il faudra que sur ce CDN, je spécifie qui a le droit d’accéder à ces fichiers. Supposons que j’autorise tout le monde, cela donnerait dans un htaccess :
Header set Access-Control-Allow-Origin "*"
Dans le billet précédent sur SubResource Integrity, j’ai fait mention de CORS pour utiliser l’outil SRI Hash Generator.
Bref, c’est bien de savoir que cela existe, mais CORS me semble un peu moins important que les autres points. Typiquement le genre de trucs que l’on peut ignorer jusqu’au jour où on s’y retrouve confronté (« mais pourquoi on n’arrive pas à accéder à mon API depuis tel autre site en AJAX ? »).
X-Content-Type-Options, X-Frame-Options, X-XSS-Protection
Ce sont d’autres en-têtes de sécurité, voici le détail pour chacun.
X-Content-Type-Options: nosniff
doit être activé sur tous vos fichiers. Cela peut se faire via un htaccess :
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
</IfModule>
Cela évite aux navigateurs de jouer aux devinettes avec les types MIME de vos fichiers, et donc de risquer des failles XSS avec des fichiers malicieux. Si les types MIME ne sont pas bons, les navigateurs ne les exécuteront pas, point barre.
Header set X-Frame-Options "DENY"
indique que vous interdisez que votre site soit embarqué dans des frame
et autres iframe
. Cela évite les attaques de type clickjacking, grosso modo qu’on embarque votre site dans un jeu de frames pour berner un utilisateur peu attentif. Si vous voulez gagner quelques points, il faut également doubler cette disposition en utilisant la directive frame-ancestors 'none';
dans vos directives CSP (voir plus bas).
X-XSS-Protection: 1; mode=block
indique aux navigateurs disposant de certains filtres de protection pour certaines attaques XSS de – s’ils détectent ces attaques – bloquer la totalité du contenu. Toujours bon à activer.
Ces trois en-têtes sont assez simples à mettre en place, c’est pour cela que j’avais beaucoup poussé pour qu’ils soient tous pris comme des bonnes pratiques Opquast dans la version 3 du référentiel. Ils y sont, les voici :
- Les en-têtes envoyés par le serveur désactivent la détection automatique du type MIME de chaque ressource
- Le serveur envoie les informations indiquant les domaines autorisés à intégrer ses pages dans des cadres.
- Le serveur envoie les informations d’activation de protection cross site scripting.
Les cookies
Pensez à spécifier le paramètre secure
et si le cookie n’a pas besoin d’être accédé par JavaScript, mettez le flag HttpOnly
à true
. Bien entendu, si vous pouvez vous passer des cookies, ou limiter leur champ d’action et leur durée de vie autant que possible, c’est préférable.
Content Security Policy (CSP)
Ah, Content Security Policy, c’est une de mes possibilités préférées depuis 2 ans. Un article de ma part devrait arriver d’ici quelque temps, donc je ne vais pas tout détailler.
Ajout du 13 Septembre : l’article vient de sortir sur Smashing Magazine, faites donc un tour sur Content Security Policy, Your Future Best Friend.
Revenons au principe : vous envoyez un en-tête CSP avec les directives que vous souhaitez voir exécutées.
Grosso modo, vous dites ce que vous allez autoriser sur votre front. Voici des ressources sur le sujet :
- Content Security Policy
- La vidéo de ma présentation sur CSP à Paris Web 2015
- Mon dépôt CSP-useful sur Github (avec des tonnes de ressources sur le sujet)
Pour avoir un bon score sur Mozilla Observatory :
- Interdisez-vous les scripts
'unsafe-inline'
(ce qui me semble être le minimum) - Mieux, n’autorisez pas les styles ou les scripts en ligne et pas de fonction
eval
(pas de'unsafe-inline'
ou'unsafe-eval'
) - Plus restrictif, interdisez-vous
default-src: 'self'
et utilisezdefault-src: 'none'
, ce qui va vous forcer à définir chaque directive à la main.
Pour obtenir cela, tout dépend d’où vous partez. Si vous avez déjà implémenté des directives CSP sans 'unsafe-inline'
ou 'unsafe-eval'
pour les styles/scripts, vous avez fait le plus dur ! Passer à default-src: 'none'
ne sera pas trop difficile, je l’ai fait en 15 minutes sur Röcssti. Par contre, si vous n’avez pas encore éliminé les styles en ligne et les scripts en ligne, le travail risque d’être plus long.
Pour vous donner un ordre d’idée, sur ce site dont le code est plutôt correct (mais ancien), j’ai eu un peu de travail pour éliminer quelques vieux cancrelats. Pas plus tard qu’hier, j’ai reçu environ 2 à 300 notifications d’erreurs CSP sur mon site (qui n’est pas monstrueusement fréquenté). Quelques mises à jour massives entre hier et aujourd’hui ont bien calmé le jeu, même si ce n’est pas totalement fini.
Par contre, sur une refonte, c’est en théorie beaucoup plus facile, vous pouvez même vous mettre de suite des garde-fous. Typiquement, c’est un long travail de propreté et d’orthogonalité dans votre code. Pour ma part, les plugins accessibles en sont une des parties les plus visibles. Sur des sites avec un existant plus compliqué, cela peut prendre plus de temps, dans ce cas, il vaudra mieux utiliser l’en-tête Content-Security-Policy-Report-Only
avec un report-uri
.
Détail, pensez bien à ajouter frame-ancestors 'none';
comme indiqué plus haut. CSP est également évoqué via une bonne pratique Opquast : le site propose un mécanisme de sécurité permettant de restreindre l’origine des contenus.
Conclusion
Ouf, si vous êtes arrivé au bout de ce billet fleuve, et moi aussi par la même occasion.
Quoi qu’il en soit, cet outil Mozilla Observatory arrive à point nommé, il met en exergue certains efforts à fournir pour proposer des sites toujours plus sûrs, et s’inscrit parfaitement dans une démarche de qualité et d’amélioration continue. Bien entendu, la sécurité de vos sites ne s’arrêtera pas là.
De mon expérience, je dois reconnaître que sans mon hébergeur actuel qui est sérieux et Let’s Encrypt, je n’aurais pas été capable d’obtenir ce A+ pour le site de Röcssti. Il y a un moment, il faut savoir reconnaître ses limites et déléguer, gérer des serveurs, c’est un métier, et je ne suis pas admin serveur non plus ! :)
Cela pose définitivement la question du sérieux des partenaires de vos sites. Mon ancien hébergeur dont j’étais plutôt content il y a quelques années – je viens de déménager ce site il y a quelques jours après des années chez eux – n’a pas voulu installer Let’s Encrypt (entre autres nombreuses conneries). La sanction est arrivée petit à petit : d’abord le site de Röcssti qui lui est passé sous le nez, ensuite celui de « Est-ce qu’on met en prod », après deux autres sites où j’avais besoin d’HTTPS, et enfin mon site personnel. Et ce n’est pas dit que je ne fasse pas déménager d’autres sites. Certes, ce sont quelques poissons noyés dans un océan, mais si tout le monde s’y met…
Ajoutons à cela qu’en tant que personnes averties, nous avons un devoir minimum d’encourager les services qui essaient de sortir du lot et qui nous écoutent. Ils ne sont malheureusement pas la majorité, parfois obtenir un simple en-tête X-Content-Type-Options: nosniff
sur certains CDN est un chemin de croix… c’est tout le sens du billet que j’ai publié chez Openweb : « chers services tiers, et si vous vous associez à nous ? ».
Le second plus gros effort à fournir est pour CSP, hormis HPKP, le reste peut s’obtenir si vous êtes capable de paramétrer quelques en-têtes.
N’hésitez pas à partager vos découvertes ou vos connaissances/infos sur ces sujets, on apprendra tous et toutes les uns des autres !
J'avais moi-même fait un peu le tour des différentes options présentées. J'avais eu un peu de mal à faire fonctionner Google Analytics http://stackoverflow.com/questions/39180486/content-security-policy-and-google-analytics-without-unsafe-inline
Tout n'est finalement pas à prendre au pied de la lettre et comme il est précisé, pour des sites déjà existants, ça peut être un peu difficile à mettre en place. Mais je trouve l(initiative très bonne car elle permet de voir nos axes d'amélioration (bon pour l'instant mon blog obtient D...