Les dernières actualités d'Alsacreations.com
Depuis quelques années nous pouvons profiter des écrans à haute densité de pixels (HiDPI - High Dots Per Inch), parfois appelés "Retina" (Apple) ou "Super-AMOLED" (Samsung) selon les marques qui les exploitent. Sur le web, c'est un peu compliqué car cela introduit de nouvelles contraintes autant pour les développeurs de sites que pour les contributeurs.
Si l'on conserve les effets graphiques et images en résolution "normale" sur un écran "HD", alors des effets de pixellisation (crénelage, effet d'escalier) peuvent apparaître :
L'inconvénient est donc qu'une image classique va être perçue comme "un peu floue" ou baver. On va vite se rendre compte de la différence de qualité rapport au texte, images vectorielles et icônes qui seront parfaitement interpolées. Pour tirer partie de la haute densité de pixels, il faudrait pouvoir diffuser une image spécifique "haute définition" pour chaque écran qui le peut.
Des avancées en HTML existent pour cela : <picture>
, srcset, sizes et en CSS nous avons les Media Queries avec le critère device-pixel-ratio
. Cependant :
Une technique consiste à prévoir dans tous les cas de figure une image plus grande que nécessaire mais aussi plus compressée que la normale, jusqu'à tenter d'atteindre le poids initial.
Attention : ce qui est présenté ici n'est pas une méthode optimale ni révolutionnaire. Il s'agit surtout d'une astuce un peu grossière pour gagner du temps si l'on ne peut pas faire autrement. Cela reste de la bidouille, l'image résidant en mémoire se trouve plus grande que nécessaire sur un écran à densité standard. Elle fut initiée par l'article Retina revolution (Netvlies.nl), suivi de Compressive Images (Filament Group).
Par exemple si la taille d'affichage est 600 x 400 pixels en JPEG compressé à 80%, alors on pourra prévoir le double 1200 x 800 en JPEG compressé à 30%. Si l'on consulte les deux images dans leurs dimensions d'origine respectives, on verra la différence de qualité due à la compression. Mais à taille équivalente, en recombinant les pixels par diminution des dimensions le navigateur pourra souvent afficher une image qui paraîtra plus nette.
Les avantages sont :
Daniel Box propose un petit test sur CodePen avec deux images pour cette démonstration :
(Les taux et critères de compression sont propres à chaque programme de traitement d'image, on suppose ici que c'est du Photoshop).
On peut remarquer qu'à poids à peu près équivalent voire légèrement moindre, la deuxième remplit parfaitement son rôle.
Nous avons fait quelques petits tests complémentaires, car il existe plein de types d'images différentes, selon qu'elles contiennent des détails, des dégradés, des motifs, des aplats quasi-unis, du texte :
On peut remarquer dans ces derniers tests plusieurs choses, en prenant des points de repère. Les captures ont été réalisée sur un écran de type MacBook Pro Retina.
Pour cette image, les détails et contours paraissent plus nets. La texture de fond (bois) est plus précise, même si au premier coup d'oeil ce n'est pas ce qui marque le plus.
Dans ce cas, c'est encore plus flagrant tout en ayant un fichier plus léger (compressé 20% = 63 Ko). Les pépins sont nettement plus précis tandis qu'on a une sensation de flou général bien perceptible sur l'image de taille "normale" (compressée à 80% = 83 Ko).
Pour ce dernier exemple, la différence est surtout ressentie sur le texte, qui bave en résolution normale et est beaucoup plus propre avec l'image grande résolution, même si sa compression est très forte (30%).
En revanche, le dégradé est à la hauteur habituelle des artefacts de compression JPEG... ce qui n'est pas souhaitable lorsqu'on tient à présenter une belle image.
Comme souvent, il est difficile d'en tirer une méthode unique. Selon le cas de figure, et selon ce qu'on cherche à obtenir, on peut jouer avec le taux de compression pour tenter d'obtenir des fichiers à peu près équivalents en poids, qui à l'écran sembleront plus nets sur des écrans à forte densité de pixels. Cela s'évalue surtout à l’œil, humain. Selon les couleurs et les formes, les ressentis pourront être très différents. Par exemple un dégradé de rouges en JPEG est souvent très pixelisé.
Rappelons que le format JPEG est indiqué pour les photos mais pas pour les images pouvant contenir du texte seulement, des motifs ou des schémas/graphiques. Il s'agira alors plutôt de s'orienter vers PNG qui est un format non destructif. Et pour le vectoriel, pensez toujours au format SVG.
C'est tout frais, ça vient de sortir : nous vous avons concocté un nouveau Quiz de connaissance dans un domaine CSS à la mode actuellement, celui de Flexbox.
Comme à l'accoutumée, le Quiz Flexbox est composé de 10 questions à réponse unique et son niveau global a été jugé "moyen" par ses concepteurs (suivez mon regard ;)).
Je vous souhaite bonne chance, à vous de jouer !
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 :
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 :
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é.
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 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 :
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é.
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.
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=16000000; 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 6 mois (15768000s = 6 mois, donc un peu plus de 6 mois 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.
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é.
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 une liste à la première connexion réussie 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 :
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 :
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 :
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.
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 les autres sites de piocher dans vos cookies.
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.
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 MIME corrects 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 XSS avec 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.
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
.
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.
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
).
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>
CSP niveau 2 ajoute de nouvelles directives :
child-src
: les sources valides pour les web workers et les éléments comme <frame>
et <iframe>
(remplace frame-src
déprécié de CSP niveau 1)form-action
: les sources autorisées utilisées dans l’attribut action
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).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 ?
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 !
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 !
L’en-tête Content-Security-Policy-Report-Only
va indiquer au navigateur de seulement rapporter les erreurs gé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 nounces 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.
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' ;
Grosso modo, j’ai tout autorisé, sauf d’embarquer le site dans des frames. 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' ;
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.
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 DareBoost testent 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 :
La spécification W3C Server Timing est destinée à envoyer des informations complémentaires dans des en-têtes HTTP, notamment pour le suivi de la performance côté serveur.
On connaît déjà bien les outils de développement navigateur (F12) et les onglets relatifs au réseau (ou Network). Ceux-ci affichent tous les timings relatifs au côté front-end, c'est-à-dire tout ce que le navigateur peut réunir comme statistiques : le temps nécessaire pour les échanges réseau, l'interprétation des codes HTML, CSS, JavaScript, l'affichage, etc.
Il est désormais possible d'y afficher aussi des informations provenant de temps de traitement côté serveur, par exemple :
Pour le moment, seul Chrome reconnaît cette fonctionnalité et Microsoft Edge y réfléchit ("under consideration"). C'est tout frais.
On peut assez facilement envoyer ces informations à l'aide d'une en-tête HTTP
Server-Timing
.
Voici un exemple d'envoi avec PHP :
header("Server-Timing: cache=0.005; \"Cache\";",false);
header("Server-Timing: fs=0.042; \"Moulinage des fichiers\";",false);
header("Server-Timing: cpu=0.048; \"Sauce piquante\";",false);
header("Server-Timing: sql=0.037; \"Connexion DB\";",false);
Ici on appelle la fonction header()
avec en 2e paramètre supplémentaire false
pour ne pas remplacer les précédents en-têtes de même nom (ce qui est le comportement par défaut), sinon seule la dernière ligne est renvoyée.
Lorsque le navigateur intercepte ces données, il peut les afficher dans les outils de développement.
Bien entendu, cela ajoute une petite complexité supplémentaire : vous devez connaître ces informations statistiques en amont du contenu pour les envoyer avec la fonction header() puisque celle-ci doit être appelée avant tout autre retour de contenu. Ceci implique qu'en général on essaiera plutôt de stocker toute la sortie dans un tampon (buffer) durant les mesures plutôt que de faire un echo, puis de renvoyer Server-Timing, puis le contenu du tampon.
Le tout est assorti d'une interface JavaScript pour collecter les données obtenues par les visiteurs et/ou les analyser automatiquement.
Vous pouvez aussi participer à l'élaboration de cette spécification qui se trouve sur Github ou suivre ses évolutions de près : http://wicg.github.io/server-timing/.