Hébergement et infogérance Open Source depuis 2004
‌ +33 1 70 61 60 16

Concilier un cache performant avec une politique de sécurité stricte de CSP avec nonce

Retour d’expérience sur la mise en place par Bearstech d’une architecture permettant l’utilisation de nonce (token unique par client) avec la mise en cache des pages par Vinyl (anciennement varnish cache) et HAProxy pour les en-têtes en évitant d’utiliser script-src ‘unsafe-inline’.

Illustration nonce haproxy et varnish

Notre prochain webinar

Explication du fonctionnement

En développement web, un nonce est un jeton ou un nombre aléatoire unique généré pour une requête ou une session spécifique, les développements de nos clients utilisent la plupart du temps des caches, dans ce cas, une fois la mise en cache générée, les visiteurs suivants reçoivent le même nonce

L’enjeu est donc de mettre en place une solution permettant de conserver un élément dynamique pour chaque visiteur, tout en gardant l’utilité du cache.

Dans le contexte spécifique du projet sur lequel nous avons travaillé, l’application web utilise Google Tag Manager, obligeant à inclure du javascript inline dynamique dans les pages html.

L’utilisation de ‘unsafe-inline’ dans une directive script-src de la Content Security Policy (CSP) est fortement déconseillée car elle désactive la protection principale de la CSP contre les failles XSS (Cross-Site Scripting). Elle autorise l’exécution de n’importe quel script écrit directement dans le code HTML.

La CSP (Content Security Policy, ou Politique de Sécurité du Contenu en français) est une couche de sécurité essentielle qui prend la forme d’un entête HTTP envoyé par le serveur au navigateur. Son objectif principal est de détecter et de bloquer les attaques par injection de code, en premier lieu les failles XSS (Cross-Site Scripting) et les injections de données.

Pour éviter d’utiliser script-src ‘unsafe-inline’ dans l’entête CSP (content-security-policy), il est possible d’utiliser un ‘nonce’, c’est à dire un token unique de 32 caractères hexadécimaux (128 bits) pour chaque réponse https.

Ce nonce étant unique pour chaque utilisateur, il ne peut pas être mise en cache par Vinyl sous peine d’être identique pour chaque utilisateur.

Normalement Vinyl ne permet pas de gérer cela (seule la version non libre le permet via l’utilisation de 2 vmods) , nous partageons avec vous comment contourner cela sans utilisation de vmod libre non packagés par Debian.

Haproxy s’occupera de mettre à jour l’entete CSP avec un nonce, de transmettre ce nonce unique à Vinyl, et Vinyl s’occupera d’injecter le même nonce dans le body (via un ESI) permettant ainsi que la page soit quand même mise en cache.

(Un ESI : Edge Side Includes est un langage de balisage (similaire au HTML) utilisé pour optimiser la mise en cache des pages web dynamiques)

Pour éviter d’utiliser script-src 'unsafe-inline' dans l’entête CSP (content-security-policy), il est possible d’utiliser un ‘nonce’, c’est à dire un token unique de 32 caractères hexadecimaux (128 bits) pour chaque réponse https.

Exemple d’entete CSP avec un script inline avec un nonce :

content-security-policy: script-src 'strict-dynamic' 'nonce-ae713b66e9a46c2e1353b44430e99818'

Script dans la page html correspondante :

<sript type="text/javascript" nonce="ae713b66e9a46c2e1353b44430e99818">
  window.alert("Hello world!");
</script>

Les navigateurs (s’ils sont compatibles avec csp) n’exécuteront le code inline que si les 2 tokens nonce sont identiques.

Référence : https://developer.mozilla.org/fr/docs/Web/HTML/Reference/Global_attributes/nonce

Documentation pour les développeurs

Cas haproxy => apache/nginx

  • haproxy injecte un header http x-nonce avec un token unique à chaque requête entrante (avec un filtre pour ne le faire que sur les pages html)
  • apache/nginx récupère l’entete et le copie dans une variable d’environnement, le rendant disponible cô@té application
  • le developpeur doit modifier l’application pour récupérer le token et peut donc ajouter le nonce à la balise du script inline concerné.

Par exemple en php :

<script type="text/javascript" nonce="<?php echo getenv('x-nonce');?>">
  window.alert("Hello world!");
</script>

Si vous utilisez le framework php symfony, il faut récuperer le token avec :

$request->headers->get('x-nonce');
  • le developpeur doit modifier l’application pour ajouter également un header csp avec un “placeholder” uniquement pour les pages contenants un script inline. Par exemple en php :
header("Content-Security-Policy: script-src 'strict-dynamic' 'nonce_placeholder'");
  • haproxy remplace automatiquement le “nonce_placeholder” par le token dans l’entête CSP de la réponse de l’application (uniquement si la réponse est de type html)

Cas haproxy => varnish-cache => apache

  • haproxy injecte un header http x-nonce avec un token unique à chaque requete entrante (avec un filtre pour ne le faire que sur les pages html)
  • le developpeur doit modifier l’application pour ajouter un header Surrogate-Capability: "abc=ESI/1.0" uniquement pour les pages contenant un script inline, pour indiquer à varnish qu’il doit exécuter les ESI . Example en php :
header("Surrogate-Capability: 'abc=ESI/1.0'");
  • le developpeur doit remplacer la balise ouvrante <script> par un ESI qui sera remplacé automatiquement par varnish (la page peut donc être en cache), L’ESI sera mis à jour dynamiquement à chaque requete. Ceci est à faire uniquement pour les pages contenant un script inline.. Exemple :
<esi:include src='/_esi-x-nonce'/>
   window.alert("Hello world!");
</script>

que varnish remplacera automatiquement en :

<script type="text/javascript" nonce="ae713b66e9a46c2e1353b44430e99818">
  window.alert("Hello world!");
</script>
  • le developpeur doit modifier l’application pour ajouter également un header csp avec un “placeholder”, uniquement pour les pages contenant un script inline, par exemple en php :
header("Content-Security-Policy: script-src 'strict-dynamic' 'nonce_placeholder'");
  • haproxy remplace automatiquement le “placeholder” par le token dans l’entête CSP de la réponse de l’application (uniquement si la réponse est de type html)

Par Pierre Arlais et Laurent Haond


L'équipe Bearstech

Inscrivez-vous à notre newsletter

Mieux comprendre le monde du DevOps et de l'administration système.

Abonnez-vous à notre newsletter

Hébergement & Infogérance

  • ✓ Service Astreinte 24h/7j/365
  • ✓ Supervision, monitoring & Alertes
  • ✓ Mises à jour en continu
  • ✓ Certificat SSL letsencrypt
  • ✓ Hébergement dédié sécurisé en France
  • ✓ Backup vers datacenter distant
Découvrir notre offre

Expertise Technologique

Notre équipe possède une vaste expertise technologique.