Security headers ou comment bien sécuriser vos applications web

Les en-têtes de sécurité - adorés par les équipes de sécurité et détestés par les développeurs - indiquent aux utilisateurs de votre application web ce à quoi ils doivent s'attendre et ce qu'elle peut faire. La question est de savoir comment pouvez-vous vous assurer que votre application dispose des bons en-têtes.

Je construis des sites statiques en utilisant Gatsby et j'ai constaté qu'un grand nombre de sites construits de cette manière n'ont pas d'en-tête de sécurité. Pour moi, une partie de la liste de contrôle de mise en service doit garantir que l'application m'offre à moi et mon entreprise et bien sûr l'utilisateur, un maximum de de sécurité. Un bon moyen d'y parvenir est de définir les en-têtes de sécurité sur votre site.

Faisons d'abord un tour pour voir ce que sont les security-headers, ensuite nous verront concrètement comment les appliquer, et enfin comment tester notre application. Ce tour n'a pas pour but d'être exhaustif, mais vous donnera les bases pour démarrer je l'espère.

Pourquoi utiliser des security headers ?

Nous utilisons des en-têtes de sécurité pour informer le navigateur des attentes de notre application. Cela couvre des choses comme :

  • Définir une liste des sources de données et de scripts externes que nous avons l'intention d'utiliser (ChatBot, Google Analytics)
  • Comment notre demande peut se présenter (Iframe, script, image)
  • Les caractéristiques de l'appareil avec lequel notre application interagit. (Geolocalisation, microphone...)

Ces en-têtes contribuent à protéger notre application, nos données et nos utilisateurs contre les attaques. La plupart des en-têtes de cet article traitent du cross-site scripting (XSS). XSS est le terme utilisé lorsque l'on injecte du code nuisible dans une application.

Un classique dans tout projet d'application web est d'utiliser les services d'une tierce partie pour effectuer des tests de pénétration ou "pentest" sur votre application. L'une des premières choses qui sera testée est l'en-tête de sécurité. Cela va de pair avec le "Top 10" de l'OWASP. Il existe donc un projet dédié aux en-têtes de sécurité de l'OWASP

Mais pourquoi est-ce important si vous générez un site statique, si vous créez votre site avec Gatsby et Prismic par exemple ? Cela dépend de ce que fait votre site (ou application). Au fur et à mesure que vous ajoutez des services externes pour les évaluations des clients, les formulaires de contact, l'intégration e-commerce, etc. ces autres fonctionnalités ajoutées peuvent vous exposer, ainsi que vos clients et votre organisation. Pour être franc, même si vous n'ajoutez pas de services externes, il y a un risque. Ce risque est facilement réduit en utilisant quelques en-têtes de sécurité de base.

Quels en-têtes de sécurité pour vos applications ?

En général, vous voulez en couvrir le plus grand nombre possible. Il n'y a cependant aucun intérêt à définir un en-tête de sécurité d'une manière totalement non sécurisée. Dans ces cas, je dirais que l'en-tête peut et doit probablement être exclu.

La liste des en-têtes de sécurité de l'OWASP est la suivante :

  1. HTTP Strict Transport Security (HSTS)
  2. Public Key Pinning Extension for HTTP (HPKP)
  3. X-Frame-Options
  4. X-XSS-Protection
  5. X-Content-Type-Options
  6. Content-Security-Policy
  7. X-Permitted-Cross-Domain-Policies
  8. Referrer-Policy
  9. Expect-CT
  10. Feature-Policy

Pour nos besoins, j'ai choisi quelques-uns des en-têtes qui ont le plus d'impact concernant le XSS. Les trois en-têtes suivants sont ceux qui font un peu plus parler d'eux et qui sont plus essentiels en matière de sécurité des applications. J'espère néanmoins pouvoir couvrir les autres rapidement.

Content-Security-Policy (CSP)

Le CSP est l'un des moyens les plus efficaces de protéger votre application contre les XSS. En bref, il établit une liste blanche des sources de contenu approuvées pour votre application. Le CSP indique au navigateur de ne pas charger d'autres sources de contenu dans notre application.

C'est un en-tête assez puissant et pas forcément facile à mettre en place pour les applications web. Il est courant pour une application web d'utiliser de nombreuses sources de contenu pour fournir des fonctionnalités, du contenu et une interface utilisateur (chatbot, google analytics, google fonts...) C'est un peu délicat car le nombre de paramètre possible est important - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy

De manière pratique, si une directive n'est pas définie, le navigateur utilisera la valeur par défaut de source.

Il y a deux choses à noter à propos de CSP :

  1. Contrairement à certains autres en-têtes, vous ne pouvez définir l'en-tête CSP qu'une seule fois. La dernière lue par le navigateur est celle qui sera utilisée.
  2. Les scripts et les styles en ligne ne sont pas compatibles avec cette règle et vous obligent soit à définir unsafe-inline, soit à effectuer un peu de logique supplémentaire.

Utiliser les styles en ligne nécessite que vous créiez un hachage du contenu. C'est en général une chose simple à faire. Chrome le rend assez facile à comprendre avec un message clair dans la console, dans ce cas pour un simple :

Validation des CSP avec hashage

Voyons un exemple si votre CSP a été défini de la sorte 'script-src': `'self' et que vous utilisez le code suivant

<script>alert('Hello world.')</script>

Le script refuse de s'éxcécuter. On nous donne néanmoins la solution, il faut passer le hash que nous devons ajouter sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=. Mais vous pouvez également obtenir ce hash en tapant la commande suivante

echo -n "alert('Hello, world.');" | openssl dgst -sha256 -binary | openssl enc -base64


Si vous n'êtes pas sûr de ce que votre CSP doit contenir, si vous êtres par exemple en train de mettre ça sur une grosse application web vous pouvez utiliser l'en-tête Content-Security-Policy-Report-Only, cela évitera de planter votre application et vous permettra d'ajuster plus facilement.
Au final les règles ne seront donc pas directement appliquées, mais un rapport à la place sera envoyé au format JSON à un endpoint que vous aurez déterminé. Ce rapport vous permettra ainsi de travailler de manière itérative sur votre politique de sécurité du contenu.
Cela peut être particulièrement utile lorsque vous utilisez des sources tierces telles que Google Tag Manager qui peut injecter du contenu provenant de plusieurs sources.

Content-Security-Policy: default-src 'self'; report-uri http://reportcollector.example.com/collector.php

Et un exemple de rapport qui peut vous être transmis

{
  "csp-report": {
    "document-uri": "http://example.com/connexion.html",
    "referrer": "",
    "blocked-uri": "http://example.com/css/style.css",
    "violated-directive": "style-src cdn.example.com",
    "original-policy": "default-src 'none'; style-src cdn.example.com; report-uri /_/csp-reports"
  }
}

Il existe encore deux possibilités pour contrôler vos scripts.

Validation avec nonce

Passer à la CSP la valeur nonce (non supporté sur I.E. petit ange parti trop tard) pour faire simple plutôt que de passer un hash complexe, l'idée est de passer une chaîne plus simple (on va éviter toto), utilisable une fois et de la faire matcher avec notre script

Content-Security-Policy: script-src 'nonce-r@nd0m' 'self';
<script nonce="r@nd0m">
	doWhatever();
</script>

Utilisation de 'unsafe-line'

Sauf dans un cas très spécifique, vous devez éviter d'utiliser le mot-clé "unsafe-inline" dans votre politique de CSP. Comme vous pouvez le deviner, il est généralement dangereux d'utiliser le mot-clé "unsafe-inline".

Unsafe-inline annule en effet la plupart des bénéfices de votre CSP. Dans ces cas là il est important de combiner ceci avec la directive self. En effet des scripts en ligne pourront potentiellement être injectés. Mais il sera par exemple impossible d'appeler cet url dans votre application.

/app?name=<script src="https://bad-guy.example.com/bad-stuff.js"></script>

c'est tout pour la CSP place à la Feature Policy / Permissions-Policy

Feature-Policy / Permissions-Policy

Feature Policy nous permet de spécifier les fonctionnalités notre application peut et ne peut pas utiliser. Cela concerne les événements à vibration, l'utilisation de la caméra et du microphone. La liste complète des éléments que vous pouvez contrôler est longue et peut être consultée sur le site des développeurs Mozilla :

Souvent appelé HSTS, cet en-tête indique à l'agent utilisateur de faire respecter l'utilisation du HTTPS, renforçant ainsi votre mise en œuvre du TLS. Sous le capot, l'objectif de cet en-tête est de rappeler à l'agent utilisateur que ce site doit être accessible sur HTTPS.

L'exemple le plus courant est l'accès à vos opérations bancaires habituelles sur Internet à partir d'un emplacement WiFi public. Tant que vous avez été sur le site web de votre banque à partir d'un endroit sécurisé (et que le HSTS a été mis en place), l'agent utilisateur saura que le site doit être accessible par HTTPS. Cela signifie que si le WiFi public est compromis d'une manière ou d'une autre, l'agent utilisateur n'acceptera pas la connexion non HTTPS.

En outre, comme l'agent utilisateur sait que l'application doit être accessible par HTTPS, il est possible d'éviter les redirections 301 inutiles pour les versions non HTTPS de l'application.

L'entête de sécurité X-Frame-Options

L'en-tête X-Frame-Options indique à l'agent utilisateur si vous souhaitez autoriser ou non votre site à être embarqué sous forme d'iframe. En empêchant l'application d'être intégrée dans un iframed, vous pouvez contribuer à réduire la probabilité d'attaques de type "clickjacking".

Comment installer les entêtes de sécurité dans un site web ?

La façon dont vous construisez et hébergez votre application a une grande influence sur la façon dont vous définissez les en-têtes de sécurité. Les différents fournisseurs d'hébergement ont différentes façons de définir les en-têtes au niveau de l'application, et les différentes stack vous offrent également différentes options. Nous allons nous intéresser ici surtout à la JamStack

Ici, je vais donc vous expliquer comment définir ces en-têtes dans une application Gatsby hébergée chez Netlify.

Mise en place via le HTML

L'une des méthodes les plus simples pour définir les en-têtes de réponse HTTP d'une page web est de les définir dans l'en-tête - <head> - du document HTML.

<meta http-equiv="Content-Security-Policy" 
      content="script-src 'self';
               style-src 'self';
               image-src ‘self;">

La meta http-equiv indique au user-agent d'utiliser le contenu fourni comme en-tête de réponse HTTP. Ce que j'ai remarqué avec cela dans Chrome, c'est que cela ne fonctionne pas toujours correctement. Vous recevrez toujours des notifications de la console si l'en-tête de votre CSP est mal défini, mais il n'apparaîtra pas nécessairement dans l'onglet Réseau lorsque vous afficherez les en-têtes de documents.

Cette approche vous permet également de définir des valeurs individuelles pour chaque page, plutôt que d'appliquer la même valeur à l'ensemble de l'application.

Comment installer les entêtes de sécurité dans Gatsby ?

Lorsque nous pensons à des générateurs de sites statiques comme Gatsby, nous envisageons la séparation de la source de données et décidons qu'ils sont sécurisés, car il n'y a pas d'accès aux données sources. En réalité, nous ajoutons des formulaires et connectons d'autres services pour créer une application plus complète. Ainsi, par exemple, nous ajoutons FormStack ou Snipcart à notre application pour ajouter des formulaires de contact ou des fonctionnalités e-commerce.

Plusieurs options sont disponibles avec les sites statiques, et certaines d'entre elles dépendent de l'endroit où vous hébergez votre application.

Via la meta http-equiv et le plugin gatsby-plugin-csp

Du point de vue content-security-policy, vous pouvez ajouter le plugin gatsby-plugin-csp. Ce plugin vous permet de configurer les parties communes de l'en-tête du CSP, mais peut également ajouter automatiquement les hachages des composants en ligne au fur et à mesure de la construction de votre application.

À titre d'exemple, voici la configuration de gatsby-plugin-csp (dans gatsby-config.js) que j'expérimentais pour mon site.

    {
       resolve: `gatsby-plugin-csp`,
       options: {
         mergeScriptHashes: false,
         mergeStyleHashes: false,
         directives: {
           'script-src': `'self' 'unsafe-inline' `,
           'style-src': "'self' 'unsafe-inline'",
           'font-src': `'self' data: db.onlinewebfonts.com`,
         },
       },
    },

Le résultat est que l'en-tête CSP est ajouté avec la meta http-equiv. L'exemple ci-dessus permet tout ce qui est HTTPS.

Définir les entêtes sur Netlify

Deux possibilités pour modifier directement les entêtes directement sur le serveur

Le fichiers _headers

Netlify utilise un fichier pour spécifier les en-têtes qui peuvent être appliqués aux applications qu'il héberge. Ce fichier est nommé _headers et se trouve dans le répertoire publish de votre site.

Lorsque j'utilise ce fichier, je le place dans mon dossier statique. En général, j'utilise un fichier assez simple, mais il peut devenir plus complexe si vous en avez besoin. Voici un exemple tiré d'un site sur lequel j'ai travaillé récemment :

/*
  X-Frame-Options: SAMEORIGIN
  X-XSS-Protection: 1; mode=block
  X-Content-Type-Options: nosniff
  Content-Security-Policy: default-src 'self' https://*.kc-usercontent.com https://*.google-analytics.com; font-src data:; style-src 'self' 'unsafe-inline' https://*.googletagmanager.com; script-src 'self' 'unsafe-inline' https://*.googletagmanager.com https://*.google-analytics.com
  Referrer-Policy: strict-origin-when-cross-origin
  Feature-Policy: geolocation 'self'
  Feature-Policy: midi 'self'
  Feature-Policy: notifications 'self'
  Feature-Policy: push 'self'
  Feature-Policy: sync-xhr 'self'
  Feature-Policy: microphone 'self'
  Feature-Policy: camera 'self'
  Feature-Policy: magnetometer 'self'
  Feature-Policy: gyroscope 'self'
  Feature-Policy: speaker 'self'
  Feature-Policy: vibrate 'self'
  Feature-Policy: fullscreen 'self'
  Feature-Policy: payment 'self'

En fonction de l'application il peut être délicat d'ajouter ce fichier _headers. Pour votre site Gatsby déployé sur Netlify je vous recommande plutôt d'utiliser directement le fichier netlify.toml à mettre à la racine de votre projet.

Le fichier .toml

[[plugins]]
  package = "@netlify/plugin-lighthouse"

  # Bonus ma configuration pour google page speed
  [plugins.inputs.thresholds]
    performance = 0.5
    accessibility = 0.5
    best-practices = 0.5
    seo = 0.9
    pwa = 0.5
# Configuration de vos CSP et plus encore
[[headers]]
  for = "/*"
  [headers.values]
    Strict-Transport-Security = "max-age=63072000; includeSubDomains; preload"
    Content-Security-Policy = "default-src data: 'unsafe-inline'  https:; script-src data: 'unsafe-inline'  https: blob:; style-src data: 'unsafe-inline' https:; img-src data: https: blob:; font-src data: https:; connect-src https: wss: blob:; media-src https: blob:; object-src https:; child-src https: data: blob:; form-action https:; block-all-mixed-content"
    X-Frame-Options = "DENY"
    X-Content-Type-Options = "nosniff"
    Referrer-Policy = "no-referrer"
    Feature-Policy = "microphone 'none'; geolocation 'none'"
    Permissions-Policy= "geolocation=(), microphone=()"

Pour en savoir plus sur la configuration des fichiers headers dans Netlify Vous avez également la possibilité de tester votre fichier de règles, ce qui peut s'avérer très pratique et vous éviter des builds inutiles.
Il est possible également d'utiliser un plugin gatsby pour vous générer ce fichier il permet également d'appliquer des règles à des pages spécifiques.

Enfin je vous recommande vivement ce site pour générer vos CSP surtout si elles sont chargées

Tester les headers de vos applications web

Lorsqu'il s'agit de tester vos en-têtes, il y a deux choses à prendre en compte :

Sont-ils vraiment là ?
Ils sont là, mais fonctionnent-ils ?

Pour la première question, le test est simple. Le plus simple est d'utiliser les outils de développement de votre navigateur pour afficher les en-têtes de réponse et voir ce qui est présent - mais vous devez savoir ce que vous recherchez.

Il existe également de nombreux outils gratuits disponibles en ligne qui analyseront votre demande et vous indiqueront les en-têtes qu'elle a trouvés. J'en citerai deux qui sont les plus utilisés :

Le vérificateur d'en-têtes de sécurité de SerpWorx et SecurityHeaders.com

Les deux sont très similaires dans leur manière de travailler. Une fois votre analyse terminée, vous obtenez plus de détails et un résumé des en-têtes qui ont été vérifiés. SecurityHeaders.com vous donnera une note de A+ à F (et R, qui devrait signifier "Run Away"), tandis que SerpWorx vous donnera une note sur 100.

Vérifier que les en-têtes fonctionnent est une tâche plus complexe. Il vous faut comprendre comment se comporte chacun des en-têtes que vous mettez en œuvre. Une fois que vous avez compris cela, vous pouvez créer des scénarios de test pour vous assurer que les en-têtes fonctionnent.

Et n'oubliez pas, vous pouvez toujours me contacter sur les réseaux ou ici si vous recherchez un développeur web sur Toulouse.

Created by potrace 1.16, written by Peter Selinger 2001-2019