Introduction
Il ne vous aura probablement pas échappé que de nombreux acteurs du Web poussent pour plus de sécurité sur les sites Internet. Pour ne citer que quatre exemples :
- les navigateurs commencent à marquer les pages contenant des formulaires comme non sécurisées et d’ici peu toutes les pages non protégées par HTTPS seront indiquées comme non sécurisées ;
- Google annonce que le HTTPS est un point pris en compte pour le référencement ;
- HTTP2 nécessite HTTPS pour pouvoir être utilisé ;
- de nouvelles APIs comme les très intéressants Service Workers ne fonctionnent pas sans HTTPS (vous en serez pour vos frais pour construire de belles PWA).
Si l’installation d’un certificat pouvait être un peu plus compliquée auparavant (le renouvellement n’était pas automatique et cela avait un coût), une initiative offrant des certificats gratuits nommée Let’s Encrypt a été lancée en bêta publique début Décembre 2015, et a connu un succès retentissant : 1 million de certificats délivrés en Mars 2015 (un mois avant que l’initiative ne sorte de sa phase de beta !!!), actuellement Let’s Encrypt a passé le cap des 20 millions de certificats actifs en ce début d’année 2017.
L’initiative est réputée pour sa simplicité et se répand de plus en plus chez les hébergeurs, c’est en général un unique clic dans leurs consoles d’administration qui vous permet d’activer un certificat TLS pleinement valide et fonctionnel. Le renouvellement étant automatique, plus besoin d’y penser et de s’en soucier.
Autrement dit, vous avez une bonne probabilité d’avoir cette possibilité à disposition… et il serait bien dommage de vous en priver, la migration vers HTTPS étant un pas à faire à un moment à un autre, le plus tôt sera le mieux (vous serez tranquilles avec cela et on n’en parle plus).
Ajoutons à cela que de nombreuses possibilités côté sécurité sont arrivées plus ou moins récemment dans les navigateurs, toutes couvrent divers risques : CDN compromis, failles XSS, attaques dites de clickjacking, transmissions non désirées d’infos via Referrer, etc. La plupart sont relativement simples à comprendre et à déployer et serviront la sécurité de vos sites et de vos utilisateurs.
Si vous pensez que le risque est faible, ayez toujours à l’esprit :
- le temps – et l’argent – que vous pourriez perdre si votre site se retrouve blacklisté ou se paie une mauvaise publicité pour un point que vous auriez pu mettre en place… à bon entendeur ! 🙂
- que le front-end reste un milieu extrêmement hostile où vous n’avez pas la main sur ce qui peut être fait de vos scripts une fois envoyés au client.
Comme toujours quand on parle de sécurité, précisons-le de suite : le concept de sécurité absolue n’existe pas. Les points abordés ici sont des briques, des outils et des bonnes pratiques parmi d’autres en matière de sécurité.
HTTPS partout et tout le temps, redirections et HSTS
Attention, bon nombre des choses énoncées ici vous font entrer dans le merveilleux monde du HTTPS, mais il sera très difficile voire quasi-impossible d’en sortir avec certains de ces outils. Si le moindre problème ou la moindre raison font que vous puissiez encore besoin de rester en HTTP, les conseils ci-dessous pourraient vous causer des soucis.
Supposons que vous ayez déjà installé un certificat (via Let’s Encrypt ou autre). Vérifiez déjà quelques points sur votre site, liste non-exhaustive :
- Pas de contenus mixtes (servi en HTTP alors que votre site est en HTTPS)
- Pas de service tiers avec des contenus mixtes ou des certificats invalides
- Pas de raison particulière à ne pas migrer vers HTTPS : un sous-domaine ne devant pas être migré, etc.
Sur certains sites (sites personnels, sites plutôt simples avec peu d’implications ou de dépendances, etc.) cette étape est en général assez aisée. Ne la négligez pas sur des sites plus complexes avec un gros existant. Certaines migrations HTTPS comme celle de Wired ou celle du Guardian sont de bons exemples : cela peut prendre beaucoup de temps et être assez compliqué.
Redirections
Maintenant que votre site peut être servi en HTTPS et que plus rien ne s’y oppose, il va falloir… ne plus rien utiliser d’autre, autrement dit votre site doit être servi uniquement en HTTPS.
Pour cela, vous pouvez commencer par mettre en place des redirections (via config serveur/htaccess ou en-tête envoyé via PHP). La logique qui est suggérée par exemple par 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.
Si l’on prend Alsacréations comme exemple : si l’on tape juste http://alsacreations.com
, cela doit d’abord renvoyer vers https://alsacreations.com
et le cas échéant ensuite vers https://www.alsacreations.com
.
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, et d’autres redirections peuvent suivre ensuite (pour ajouter le www., etc.). Toutefois, cela n’est pas encore suffisant.
HTTP Strict Transport Security
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 :
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 un peu plus de 1 an (31536000s = 1 an dans cet exemple) 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 grâce à la directive includeSubDomains
(retirez-la si vous ne voulez pas que ce soit le cas).
C’est pour cela que par exemple vous aurez l’impression chez certains hébergeurs 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 pendant la durée spécifiée.
HSTS preload list
La seule limite de l’en-tête HSTS est la première connexion : tant qu’on ne l’a pas reçu, on n’est pas protégé par ses bienfaits. On parle du principe joliment nommé TOFU (non, ce n’est pas de la sécurité vegan) : on fait confiance à la première utilisation.
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. Si vous ne souhaitez pas y être inclus, ne le mettez pas. Attention, le processus prend déjà un certain temps pour être inscrit, et tout autant de temps pour en être retiré.
HTTP Public Key Pinning
Danger : ce point est 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. C’est pour cela que nous en resterons à l’évocation de cette possibilité sans aller trop loin.
Grosso modo, l’idée de l’en-tête HPKP est de faire présenter à la première connexion réussie une liste représentant les clés de confiance par le serveur (via un hashage cryptographique, un SHA-256 par exemple). 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.
Cet en-tête est là pour éviter des attaques dites MITM quand une autorité de certification est compromise. En voici un exemple :
Public-Key-Pins:
pin-sha256="cUPcTAZWKaASuYWhhneDttWpY3oBAkE3h2+soZS7sWs=";
pin-sha256="M8HztCzM3elUxkcjR2S5P4hhyBNf6lHkmjAHKhpGPWE=";
max-age=5184000; includeSubDomains;
report-uri="https://www.example.org/hpkp-report"
Je le redis (et les ressources ci-dessous aussi) : faites vraiment attention avant de déployer HPKP, procédez doucement en mode Public-Key-Pins-Report-Only
, car il est très facile de faire des erreurs et de mettre hors jeu son propre site si on ne fait pas attention. N’utilisez pas bêtement l’outil ci-dessous, 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).
Une preuve de ces risques : Smashing Magazine en a fait les frais il y a quelque temps, c’est raconté dans Be afraid of public key pining, et la suite How To Issue A New SSL Certificate With An Old SSL Key.
Pour en savoir plus :
- HTTP Public Key Pinning (by Scott Helme)
- HTTP Public Key Pinning (Mozilla Developer Network)
- HTTP Public Key Pinning (en français, chez Stéphane Bortzmeyer)
SubResource Integrity (SRI)
SubRessource Integrity (en bon français le «contrôle d’intégrité des sous-ressources») est une spécification dont les discussions ont commencé début 2014 et qui est devenue une recommandation officielle au W3c le 23 juin 2016.
Son but part d’un constat très simple : si vous utilisez des ressources sur un CDN (ce qui peut être une très bonne pratique dans certains cas) et que ce dernier se retrouvait compromis, votre site se retrouverait – bien malgré lui – soit compromis, soit vecteur de fichiers malveillants pour ses utilisateurs (voir par exemple le défaçage du site de jQuery en 2014).
Il faut donc vérifier que les fichiers n’aient pas été modifiés entre temps, c’est là que SRI intervient : cela consiste à indiquer un attribut integrity
sur les balises script
et/ou link
– les seules supportées pour le moment – et cet attribut contiendra un hash d’intégrité du fichier, que le navigateur vérifiera avant d’exécuter les scripts ou d’appliquer les styles.
Cette valeur commence par au moins une chaîne (il peut y avoir plusieurs valeurs d’intégrité spécifiées, séparées par des espaces), chaque chaîne comprenant :
- un préfixe indiquant l’algorithme utilisé (aujourd’hui, les préfixes autorisés sont sha256, sha384 et sha512) ;
- suivi d’un tiret…
- …et se terminant par le hash proprement dit (encodé en base64).
Par exemple, cela nous donnerait :
<script src="kiwi.js" integrity="sha384-yC7kyQdHbdC5s22r6n/XRG6mserieuxVousLisezLesHashPMQnhBld5"></script>
Cette valeur peut se calculer de plusieurs manières. Soit à la ligne de commande :
cat FILENAME.js | openssl dgst -sha384 -binary | openssl enc -base64 -A
Ou en PHP en supposant que $input
soit le contenu du fichier :
echo base64_encode(hash(’sha384’, $input, true));
Cela peut aussi se calculer avec des outils comme SRI hash.
Voici deux exemples simples d’utilisation de SRI :
Dans le premier cas, le calcul d’intégrité est bon, la CSS est appliquée.
Dans le second, la valeur fournie ne correspond pas : le navigateur refuse d’appliquer les styles.
En général, les bibliothèques et autres scripts mis à disposition sur des CDNs vous proposent SRI, exemple avec jQuery :
Le support étant plutôt bon et ne gênant pas les navigateurs ne le supportant pas, vous pouvez déployer massivement SRI sans aucun souci.
Pour en savoir plus :
- SubResource Integrity (en français)
- SRI test
Referrer Policy
Quand un utilisateur navigue vers un autre site via un lien ou si un site charge une ressource externe, les navigateurs informent le site de destination de l’origine de la requête en utilisant l’en-tête HTTP Referrer. Si cela peut être utile dans certains cas (les referrers dans certains outils de statistiques se basent là-dessus), cela peut aussi poser des problèmes de confidentialité et de sécurité.
Prenons un exemple, observons la page d’accueil d’Alsacréations. On peut voir qu’elle charge une webfont via un GET https://fonts.googleapis.com/css?family=Montserrat:400,700
. Si l’on regarde les en-têtes de la requête, on voit le referrer avec l’adresse d’Alsacréations. Sur la page des tutoriels, idem.
En clair : le serveur font.googleapis.com sait que la requête vient de cette adresse via le referrer.
Évidemment, cet exemple n’est pas bien méchant. Imaginez que l’on soit sur une partie privée d’Alsacréations, imaginons www.alsacreations.com/une-url-qu-on-veut-garder-secrete (le plan de domination mondiale de Raphaël Goetter pour rallier tous les kiwis, chut, cela reste entre nous). Est-ce que l’on souhaiterait que des ressources externes soient au courant de cette adresse ?
Plus inquiétant, imaginons une page concernant des infos sensibles dans l’URL (ou étant un site sensible en soi), mettons un site d’entraide entre personnes atteintes d’un cancer, et imaginons qu’un lien sur cette page mène vers un site externe (un utilisateur peu prudent qui parle du site de son entreprise). Mettez-vous à la place de vos visiteurs : est-ce que vous pensez qu’ils souhaitent laisser ce genre de traces et potentiellement mettre au courant la page de destination qu’ils viennent de ce site ?
Bien sûr, cela dépend, je pense que si je mets un lien depuis mon site personnel vers Alsacréations, je n’ai pas de problème à ce qu’Alsacréations soit au courant dans ses logs qu’un lien vient de chez moi. Néanmoins, vous devez, en tant que personnes responsables pour vos utilisateurs, vous poser cette question pour eux.
Voici les valeurs possibles :
Referrer-Policy: "no-referrer"
: aucun en-tête referrer n’est envoyé.Referrer-Policy: "no-referrer-when-downgrade"
(valeur par défaut) : le referrer est envoyé si la sécurité de la destination est à priori égale (HTTPS→HTTPS), mais n’est pas envoyée pour une destination moins sécurisée (HTTPS→HTTP).Referrer-Policy: "origin"
: seule l’origine du document sera envoyée dans tous les cas. https://www.alsacreations.com/tutoriels/ enverra le referrer https://www.alsacreations.com/.Referrer-Policy: "origin-when-cross-origin"
: envoie l’URL complète si la destination a la même origine, et seulement l’origine pour les autres cas.Referrer-Policy: "same-origin"
: le referrer sera envoyé pour les URL avec la même origine, et aucun referrer ne sera envoyé dans les autres cas.Referrer-Policy: "strict-origin"
: le referrer ne contiendra que l’origine du document si la sécurité de la destination est à priori égale (HTTPS->HTTPS), rien ne sera envoyé pour une destination moins sécurisée (HTTPS→HTTP).Referrer-Policy: "strict-origin-when-cross-origin"
: quand la requête a la même origine, l’URL complète est envoyée en referrer, dans le cas contraire : le referrer ne contiendra que l’origine du document si la sécurité de la destination est à priori égale (HTTPS→HTTPS), rien ne sera envoyé pour une destination moins sécurisée (HTTPS→HTTP).Referrer-Policy: "unsafe-url"
: l’URL complète est passée en referrer, quel que soit le cas (déconseillé).
Voici un exemple envoyé par PHP :
header('Referrer-Policy: strict-origin-when-cross-origin');
Bref, c’est à définir et surtout à adapter selon votre cas.
À noter : Mozilla Observatory suggère d’utiliser comme valeur soit "no-referrer"
, soit "same-origin"
, soit "strict-origin"
ou soit "strict-origin-when-cross-origin"
. Le W3c vous recommande de définir une politique de Referrer.
Pour en savoir plus :
- Referrer Policy (en français)
Cookies
On a tendance à les oublier, mais il serait bête de sécuriser tout votre site… et de ne pas le faire pour vos cookies. Ces derniers peuvent contenir des informations sensibles, comme des informations de sessions. Et ils peuvent également être créés/manipulés côté front, par JavaScript.
Prenons comme base de départ la fonction PHP qui crée un cookie :
bool setcookie ( string $name [, string $value [, int $expire = 0 [, string $path [, string $domain [, bool $secure = false [, bool $httponly = false ]]]]]] )
Bien entendu, une portée (chemins ET domaines) et une durée de vie adéquates (comprenez «raisonnablement limitées») sont un premier bon réflexe. Intéressons-nous aux deux derniers paramètres qui sont dédiés à la sécurité :
$secure
spécifié à true indiquera à PHP de ne transmettre les cookies que via une connexion sécurisée par HTTPS.$httponly
interdira l’utilisation du cookie côté client.
Comme cela ne coûte rien d’utiliser ces possibilités, ne vous gênez pas pour. 🙂
Pour en savoir plus : Sécurisez vos cookies (instructions Secure et HttpOnly)
À noter, de nouveaux paramètres arrivent pour sécuriser les cookies, notamment sameSite
qui interdira aux autres sites de piocher dans vos cookies.
CORS
CORS est un en-tête HTTP indiquant votre politique concernant les sites souhaitant utiliser vos ressources directement depuis un autre nom de domaine, via des méthodes comme XMLHttpRequest.
Par exemple, supposons que l’on veuille mettre en place un CDN avec quelques bibliothèques JavaScript à disposition. Il faudra que, sur ce CDN, soit spécifié qui a le droit d’accéder à ces fichiers.
Supposons que l’on autorise tout le monde, cela donnerait dans un htaccess :
Header set Access-Control-Allow-Origin "*"
Si l’on ne veut autoriser qu’un nom de domaine particulier à utiliser ces ressources :
Access-Control-Allow-Origin: https://kiwi.alsacreations.com
C’est bien de savoir que cela existe, mais CORS est typiquement le genre de truc que l’on peut ignorer jusqu’au jour où on en a vraiment besoin : s’il n’y a pas de besoin spécifique, ignorez-le (en général, l’idée est plutôt d’ouvrir les possibilités car elles sont restreintes par défaut).
X-Content-Type-Options
, XFO
, X-XSS-Protection
Ces trois en-têtes HTTP sont d’autres en-têtes de sécurité. Tous peuvent être utilisés via un htaccess ou envoyés via la fonction header
de PHP par exemple.
X-Content-Type-Options
Internet Explorer, Chrome et Firefox 50+ ont la possibilité de «sniffer» des fichiers qui n’ont pas de type MIME correct pour les exécuter. Si l’idée peut sembler pratique, elle est source de beaucoup de dangers : imaginons qu’un attaquant utilise un fichier avec une extension «innocente» mais contenant du JavaScript, cela pourrait créer des failles de type XSS. Pour éviter cela, X-Content-Type-Options: nosniff
doit être activé sur tous vos fichiers, et votre serveur doit indiquer les types MIMEcorrects pour les fichiers. Cela peut se faire par exemple via un htaccess :
<IfModule mod_headers.c>
Header always set X-Content-Type-Options "nosniff"
</IfModule>
Cela évite donc aux navigateurs de jouer aux devinettes avec les types MIME de vos fichiers, et donc de risquer des failles XSSavec des fichiers malicieux. Si les types MIME ne sont pas bons, les navigateurs ne les exécuteront pas et ne risqueront pas de faire de bêtise.
X-Frame-Options
Une attaque dite de type clickjacking consiste à embarquer votre site dans un jeu de frames pour berner un utilisateur peu attentif. L’en-tête X-Frame-Options
est là pour éviter cela. Il peut s’envoyer par exemple via htaccess :
Header set X-Frame-Options "DENY"
X-Frame-Options "DENY"
indique que vous interdisez que votre site soit embarqué dans des frame
et autres iframe
, purement et simplement. La valeur SAMEORIGIN
permet au site de s’embarquer lui-même uniquement. Il existe bien une valeur ALLOW-FROM uri
mais elle est dépréciée.
Cet en-tête a un comportement plus ou moins limité, il sera à terme remplacé par la directive frame-ancestors
de CSP(que nous verrons plus loin), qui elle permet d’indiquer en détail les sites autorisés à embarquer le vôtre. Toutefois, comme frame-ancestors
n’est pas supporté par IE≤11 (ça va arriver bientôt dans Edge), Safari 9.1 (desktop) et Safari 9.2 (iOS), cet en-tête X-Frame-Options
est toujours bon à utiliser.
Pour l’instant, autant même utiliser conjointement X-Frame-Options
ET la directive CSP frame-ancestors
.
X-XSS-Protection
Certains navigateurs ont la possibilité de détecter certaines attaques XSS dites reflected, qui consistent à injecter du code exécutable par le navigateur dans une chaîne d’attaque qui est incluse dans l’URI ou les paramètres HTTP, est mal interprêtée puis renvoyée à la victime. Exemple, passer un paramètre http://example.com/page?var=<script>alert('xss')</script>
à un site qui n’échappe pas ses données.
Typiquement, les développeurs d’Alsacréations connaissent bien le risque des données insérées par les utilisateurs, car si vous recherchez <script>alert('xss')</script>
, cette chaine sera échappée et rien ne sera exécuté. 😉
X-XSS-Protection: 1; mode=block
indique aux navigateurs d’activer ces filtres de protection pour certaines attaques XSS et – s’ils détectent ces attaques – de bloquer la totalité du contenu. Toujours bon à activer.
Content Security Policy
CSP est un en-tête HTTP dont le principe est simple : vous envoyez des directives de sécurité au navigateur pour vos sources de contenus et vos éléments front-end, et ce dernier va se charger de les appliquer à la lettre.
L’idée de base de CSP est de réduire la surface d’attaque face aux attaques de type XSS. En pratique, CSP permet d’aller beaucoup plus loin et offre d’énormes possibilités : outils, connaissances et surveillance de votre front-end. Nous n’allons pas tout détailler ici, il y aurait de quoi écrire un roman 🙂
Voici un exemple d’en-tête envoyé via PHP :
header("Content-Security-Policy: <ici vos directives>");
Voici quelques exemples de directives :
default-src 'self' ;
default-src
sera la directive appliquée si aucune directive n’est définie pour un élément. Le mot-clé self
indique que tout ce qui sera sur le même port, même protocole et même nom de domaine sera autorisé.
script-src 'self' www.piwik.com ;
Dans cet exemple, la directive script-src
indique donc self
, et également que tous les fichiers JavaScript provenant de www.piwik.com
seront autorisés à s’exécuter.
img-src 'self' data: ;
Cette directive img-src
indique également self
, le mot-clé data:
autorise quant à lui les contenus dit embarqués (via data-uri
).
Liste des principales directives
CSP niveau 1 permet de spécifier des directives pour les éléments suivants :
script-src
: les sources autorisées pour les scripts JSstyles-src
: les sources autorisées pour les styles CSSimg-src
: les sources autorisées pour les imagesconnect-src
: s’applique pour XMLHttpRequest (AJAX), WebSocket ou EventSourcefont-src
: les sources pour les web fontsobject-src
: les sources des plug-ins (par exemple :<object>
,<embed>
,<applet>
)media-src
: les sources pour les balises<audio>
et<video>
- etc.
CSP niveau 2 ajoute de nouvelles directives :
child-src
: les sources valides pour les web workers et les éléments comme<frame>
et<iframe>
(remplaceframe-src
déprécié de CSP niveau 1)base-uri
: les sources valides pour l’élément base qui indique l’adresse à utiliser pour recomposer toutes les adresses relatives contenues dans la page (<base href="…">
)form-action
: les sources autorisées utilisées dans l’attributaction
d’un formulaireframe-ancestors
: les sources autorisées à embarquer la ressource dans<frame>
,<iframe>
,<object>
,<embed>
ou<applet>
upgrade-insecure-requests
: indique à l’agent utilisateur de réécrire l’URL en changeant HTTP en HTTPS (pour les sites avec beaucoup d’anciennes URLs qui ont besoin d’être réécrites).- etc.
Un exemple pratique
Voici un exemple présenté lors de la conférence «Codeurs en Seine» en 2016, cette page contient des éléments indésirables. Sans CSP, voici le résultat :
Pas génial. Envoyons des directives :
content-security-policy:
default-src 'none' ;
script-src 'self' www.google-analytics.com ;
style-src 'self' ;
img-src 'self' www.google-analytics.com data: ;
font-src 'self';
frame-ancestors 'none' ;
Que va-t-il se passer ? Le navigateur les reçoit et va les appliquer à la lettre selon la règle suivante : tout ce qui n’est expressément autorisé dans les directives CSP sera interdit. Par interdit, comprenez : bloqué, non exécuté, non affiché.
Point important : par défaut, le JavaScript en ligne n’est pas autorisé. Cela aurait pu se faire avec script-src 'unsafe-inline'
et via script-src 'unsafe-eval'
, toutefois, c’est déconseillé (n’oublions pas que le but de CSP est de limiter les failles XSS).
Et il en va de même pour la fonction eval
de JavaScript, ainsi que pour les styles en ligne. Autrement dit, ces directives qui semblaient être plutôt cools sont en fait très strictes et autorisent le minimum nécessaire.
On pourra retrouver dans la console les éléments bloqués et le pourquoi de ce blocage, exemple :
Voici le résultat :
Mieux, n’est-ce pas ?
Déployer CSP
Sauf si vous êtes sur un environnement de développement où vous pouvez vous permettre toutes les bêtises que vous voulez, un conseil : ne déployez pas CSP à l’aveuglette avec la fleur au fusil. Ne le faites jamais, car vous risquez de très vite déchanter.
CSP est en fait un redoutable révélateur de votre maitrise du front-end de votre projet. Avez-vous des styles ou des scripts en ligne ? Connaissez-vous toutes vos sources de contenus ? Savez-vous exactement ce que fait chaque composant/service tiers/etc. ? Vous pourriez avoir des surprises !
Report-uri
Heureusement, il existe des possibilités et des stratégies pour déployer CSP progressivement, et sans prendre trop de risques. La première et probablement une des plus pratiques est le Report-uri : vous pouvez spécifier une adresse vers laquelle le navigateur va envoyer les notifications CSP. Par exemple, vous pouvez spécifier dans vos directives :
report-uri /csp-parser.php ;
Cette adresse recevra les notifications CSP. Voici 3 exemples de scripts qui pourront les traiter (basique, avec des filtres, avec une base de données).
Attention sur une production très visitée ! Pour prendre un exemple, si une page génère 100 erreurs CSP, et que cette page est visitée 100 fois par jour, rien que cette page vous enverra 10 000 notifications par jour !
Report-only
L’en-tête Content-Security-Policy-Report-Only
va indiquer au navigateur de seulement rapporter les erreursgénérées par les directives spécifiées, sans bloquer quoi que ce soit côté navigateur.
Cela va vous permettre de tester des directives sans danger de bloquer des contenus importants sur votre site. Couplée avec Report-uri, vous pouvez en plus savoir tout ce qu’il se passe sur votre front-end vis à vis de CSP (ce qui vous permettra d’être au courant en cas de tentative de faille XSS.
Plein d’autres possibilités existent, notamment les hashes et les nonces mais tout détailler serait trop long. Si le sujet vous intéresse, je vous invite à lire Content Security Policy sur Openweb, qui détaille tout cela.
Diverses stratégies
Il existe plusieurs niveaux de difficulté et plusieurs stratégies pour déployer CSP sur votre site.
Soit vous connaissez bien votre front-end (maitrise et/ou site assez simple), chaque composant que vous utilisez est connu sur le bout des doigts, dans ce cas, vous aurez probablement une idée assez précise de ce que vous devez autoriser dans les directives CSP. Testez vos directives via Content-Security-Policy-Report-Only
, réglez les éventuels soucis et cela sera vite terminé.
Soit vous avez une connaissance moins fine de votre front-end, dans ce cas, je vous conseille d’y aller progressivement. Vous pouvez déjà commencer à tester des directives très permissives :
Content-Security-Policy: default-src * data: ; script-src * 'unsafe-inline' 'unsafe-eval' ; style-src * 'unsafe-inline' data: ; frame-ancestors 'none' ; base-uri 'self' ;
Grosso modo, j’ai tout autorisé, sauf d’embarquer le site dans des frames (et base-uri
que j’ai mis à 'self'
, si vous ne vous servez pas de ce tag, mettez la valeur à 'none'
). Après, vous pouvez par exemple diminuer les sources de contenus en étant encore très permissif :
Content-Security-Policy: default-src 'self' www.foo.com data: ; script-src 'self' www.foo.com 'unsafe-inline' 'unsafe-eval' ; style-src 'self' www.foo.com 'unsafe-inline' data: ; frame-ancestors 'none' ; base-uri 'self' ;
Là, je n’ai autorisé que self
et divers noms de domaines, ce qui réduit déjà bien la voilure. Une prochaine étape serait de s’interdire les styles en ligne en enlevant unsafe-inline
dans style-src
. Idem pour le JavaScript si c’est possible (ou de s’interdire l’utilisation de eval
en enlevant unsafe-eval
. La dernière étape ressemblerait à :
Content-Security-Policy: default-src 'none' ; …
En clair, par défaut, on n’autorise rien, ce qui revient à devoir définir chaque directive très précisément (attention à ne pas en oublier une !).
Quoi qu’il en soit, vous pouvez avoir des directives en production et tester en même temps des directives plus dures en report-only.
Les possibilités et usages de CSP sont innombrables, nous n’avons fait que les survoler dans ce tutoriel. Si le sujet vous intéresse, je vous invite à jeter un oeil à CSP useful, un dépôt sur Github où j’ai collecté beaucoup d’informations sur le sujet.
Conclusion et outils
HTTPS est et sera un sujet incontournable cette année, vous aurez du mal à y échapper.
De nombreux et très bons outils comme Mozilla Observatory (qui en rassemble d’autres), Security Headers ou DareBoosttestent et peuvent vous conseiller des améliorations, avec moult informations et explications fort bienvenues.
À noter, certaines de ces technologies sont encore plus efficaces ensemble, et peuvent même travailler de concert (notamment CSP et SRI, etc.).
Comme vous avez pu le voir lors de ce long tutoriel, certains points sont très simples à mettre en production et peuvent déjà se mettre au service de vos utilisateurs et/ou de vos sites, ne vous gênez pas pour commencer !
Voici quelques ressources :
- OWASP
- Security/Guidelines/Web Security
- Mozilla Observatory
- DareBoost
- Hardenize
- Security Headers
- Content Security Policy
- SubResource Integrity
- Collection of scripts, thoughts about CSP (Content Security Policy)
Photo credit: The Preiser Project via Visualhunt / CC BY
Source : Alsacreations.com