Cloudflare : comment sécuriser votre serveur d'origine ?

Cloudflare s’est imposé comme un acteur incontournable sur le web ces dernières années, offrant non seulement des services de CDN, mais aussi une protection contre diverses attaques. Néanmoins, pour que ces protections soient pleinement efficaces, il est essentiel que le serveur soit correctement configuré.

En effet, il est crucial d’empêcher les attaquants d’accéder directement aux serveurs hébergeant les applications web. Pour ce faire, les utilisateurs doivent impérativement passer par Cloudflare, qui se chargera de rediriger le trafic vers le serveur en fonction des règles spécifiques définies par le client.

De cette manière, Cloudflare agit comme un intermédiaire sécurisé, transformant l’architecture traditionnelle client <-> serveur en une architecture client <-> Cloudflare <-> serveur.

Dans cet article, nous explorerons plusieurs configurations Cloudflare permettant de mettre en place cette architecture sécurisée et mettrons en lumière certains points faibles associés à certaines de ces solutions.

Guide complet : configurations Cloudflare pour sécuriser le serveur d’origine

Exemple d’exploitation d’une faille XSS malgré l’implémentation de Cloudflare

Pour illustrer nos explications, nous utiliserons une application PHP composée de deux parties :

  • Une requête POST vers « add_comment.php » pour publier un commentaire.
  • Une requête GET vers « display_comment.php » pour récupérer les commentaires.

Voici la requête pour poster un commentaire :

POST /add_comment.php HTTP/1.1
Host: vaadata.example
[…TRUNCATED HEADER…]

author=hacker42&message=test1234

Réponse :

HTTP/1.1 200 OK
[…TRUNCATED HEADER…]

    <p>The message below has been successfully sent test12345</p>

Ensuite, un administrateur peut consulter les derniers commentaires avec la requête suivante :

GET /display_comment.php HTTP/1.1
Host: vaadata.example
[…TRUNCATED HEADER…]

Réponse :

HTTP/1.1 200 OK
[…TRUNCATED HEADER…]

<h1>Unread message</h1>
<table>
 <thead>
    <tr>
      <th scope="col">Author</th>
      <th scope="col">Message</th>
    </tr>
  </thead>
<tbody>
<tr><th scope='row'>hacker42</th><td>test1234</td></tr>
</tbody>

</table>

Ici, même si le trafic de « vaadata.example » transite par Cloudflare, aucune règle spécifique n’a été définie sur le pare-feu d’application web (WAF) de Cloudflare.

En conséquence, une payload XSS soumise par un attaquant ne sera pas bloquée. Par ailleurs, l’application n’effectue pas d’encodage des données, ce qui rend le site entièrement vulnérable aux attaques XSS.

Requête :

POST /add_comment.php HTTP/1.1
Host: vaadata.example
Accept-Language: fr-FR
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.6478.127 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: application/x-www-form-urlencoded
Content-Length: 53

author=hacker42&message=<script>alert('XSS')</script>

Réponse :

HTTP/1.1 200 OK
Host: vaadata.example
Date: Tue, 02 Jul 2024 15:37:22 GMT
Connection: close
X-Powered-By: PHP/8.3.8
Content-type: text/html; charset=UTF-8

    <p>The message below has been successfully sent <script>alert('XSS')</script> </p>

L’administrateur qui accède au message sera également piégé :

Face à cette situation, l’administrateur choisira simplement d’activer le WAF de Cloudflare (car c’est bien plus facile que de s’attaquer à la source du problème) :

Cela aura bien pour effet de bloquer la requête de l’attaquant :

POST /add_comment.php HTTP/1.1
Host: vaadata.example
[…TRUNCATED HEADER…]

author=hacker42&message=test12345<script>alert('XSS')</script>

NB : Cloudflare doit être considéré comme une couche de défense en profondeur et non comme une solution de sécurité unique. Bien que cet article ne se concentre pas sur les moyens de contourner Cloudflare, il est important de noter que de telles possibilités existent.

Par exemple, certains contournements peuvent être réalisés en soumettant des données qui dépassent une certaine longueur de caractères, ce qui pourrait potentiellement passer outre certaines protections.

Requête :

POST /add_comment.php HTTP/1.1
Host: vaadata.example
[…TRUNCATED HEADER…]
Content-Length: 40867

author=hacker42&message=AAAAAAAAAAAAAAAAAAAAA[…TRUNCATED DATA]AAAAAAAAAAAAAAAAAAAAA <script>console.log('XSS')</script>

Réponse :

HTTP/1.1 200 OK
[…TRUNCATED HEADER…]

    <p>The message below has been successfully sent […TRUNCATED DATA]

Contourner Cloudflare

Lorsqu’un attaquant cible un site protégé par Cloudflare, l’une des premières étapes consiste souvent à découvrir l’adresse IP du serveur d’origine. Plusieurs méthodes peuvent être utilisées pour y parvenir :

  • Analyser l’historique des enregistrements DNS.
  • Effectuer une recherche basée sur le contenu ou le favicon du site.
  • Exploiter une fuite de l’adresse IP du serveur.

Ici, nous admettons que l’attaquant a trouvé l’adresse réelle du serveur pendant sa reconnaissance : 256.256.256.256. L’adresse IP n’appartient pas à la plage autorisée, mais c’est juste pour l’exemple.

Maintenant il suffit de changer l’adresse d’origine pour effectuer l’attaque :

Requête :

curl --path-as-is -i -s -k -X $'POST' \

    -H $'Host: vaaata.example' -H $'User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:127.0) Gecko/20100101 Firefox/127.0' -H $'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8' -H $'Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3' -H $'Accept-Encoding: gzip, deflate, br' -H $'Connection: keep-alive' -H $'Upgrade-Insecure-Requests: 1' -H $'Priority: u=1' -H $'Content-Type: application/x-www-form-urlencoded' -H $'Content-Length: 68' \

    --data-binary $'author=hacker42&message=test12345<script>alert(\'XSS\')</script>' \

    $'http://256.256.256.256/add_comment.php'

Réponse :

HTTP/1.1 200 OK
[…TRUNCATED HEADER…]

    <p>The message below has been successfully sent test12345<script>alert('XSS')</script> </p>

Ici, il est évident que si le serveur n’est pas correctement configuré, la protection peut être facilement contournée dès lors que l’adresse IP du serveur est découverte.

Dans la suite de cet article, nous allons explorer les mesures à mettre en place pour sécuriser le serveur d’origine contre ces attaques.

Quelles sont les différentes configurations de Cloudflare pour sécuriser le serveur d’origine ?

Une approche évidente pour sécuriser le serveur d’origine est de restreindre l’accès exclusivement aux adresses IP appartenant à Cloudflare. Cette mesure peut être mise en œuvre directement au niveau du pare-feu du serveur.

Ainsi, l’attaquant ne pourra pas cibler directement le serveur, car il ne recevra aucune réponse de ce dernier et sera donc contraint de passer par Cloudflare.

L’avantage de cette approche réside dans sa simplicité de mise en place. Cependant, elle n’est pas infaillible. D’après la documentation de Cloudflare, cette configuration peut être contournée par des techniques telles que l’IP spoofing. De plus, un attaquant peut relativement facilement mener une attaque en utilisant une adresse IP appartenant à Cloudflare, bien que cela nécessite de suivre certaines étapes spécifiques.

Pour ce faire, l’attaquant commence par enregistrer un domaine sur Cloudflare, puis configure les DNS de ce domaine pour rediriger le trafic vers l’adresse IP du serveur qu’il a découvert.

Enfin, il injecte sa payload XSS directement sur son sous-domaine « hacker.example« . Cela aura pour effet d’envoyer l’attaque via Cloudflare, qui la redirigera ensuite vers le serveur légitime en utilisant une adresse IP appartenant à Cloudflare, et donc autorisée par le pare-feu.

Requête :

POST /add_comment.php HTTP/1.1
Host: hacker.example
[…TRUNCATED HEADER…]

author=hacker42&message=<script>alert('XSS')</script>

Réponse :

HTTP/1.1 200 OK
[…TRUNCATED HEADER…]

    <p>The message below has been successfully sent<script>alert('XSS')</script> </p>

Ainsi il est possible de réinjecter des XSS sur l’application.

NB : À partir de maintenant, nous supposerons que « hacker.example » garde cette configuration.

À noter que le choix du host est limité, et qu’une méthode pour bloquer la requête est de vérifier le host côté origine.

Une autre solution proposée par Cloudflare consiste à ajouter un en-tête personnalisé (custom header) et à vérifier sa présence côté applicatif.

Par exemple, l’administrateur de « vaadata.example » peut ajouter la règle suivante :

Puis, il va rajouter du code sur son application PHP pour vérifier la présence de l’en-tête :

<?php

if (!isset($_SERVER['HTTP_CHECK_CLOUDFLARE']) || $_SERVER['HTTP_CHECK_CLOUDFLARE'] === 'yes') {
    header('HTTP/1.0 401 Unauthorized');
    die; 
}

À noter que le nom et la valeur du header personnalisé sont arbitraires.

Ainsi, l’accès au site « vaadata.example » retourne désormais correctement « Hello World ». Cependant, toute tentative d’accès à l’application via son adresse IP ou via un autre domaine pointant vers l’adresse IP du serveur, comme illustré dans la section précédente, ne fonctionnera plus.

Requête :

GET / HTTP/1.1
Host: hacker.example
[…TRUNCATED HEADER…]

Réponse :

HTTP/1.1 401 Unauthorized
[…TRUNCATED HEADER…]

Cette méthode a cependant plusieurs inconvénients :

  • Cela peut être lourd à mettre en place côté applicatif, car il faut s’assurer que chaque page vérifie la présence du header. Cependant, cette tâche peut être simplifiée dans le cas où la vérification peut être centralisée au niveau du serveur web. De même, l’utilisation d’un middleware dans l’application peut automatiser ce contrôle de header pour toutes les requêtes.
  • Si le header personnalisé fuit, un attaquant peut le découvrir et l’ajouter à ses requêtes. Par exemple, dans ce cas, l’attaquant a trouvé la page « phpinfo4256.php« , qui divulgue le header ‘HTTP_CHECK_CLOUDFLARE‘.

Il lui suffit alors d’ajouter ce header à ses requêtes :

GET / HTTP/1.1
Host: hacker.example
CHECK_CLOUDFLARE: yes
[…TRUNCATED HEADER…]

Réponse :

HTTP/1.1 200 OK
[…TRUNCATED HEADER…]

<!doctype html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>Titre de la page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>

De fait, il est crucial de chiffrer le trafic avec SSL (HTTPS), car si les requêtes ne sont pas sécurisées, elles peuvent être interceptées par un attaquant.

Authentification mTLS (mutual TLS) : principes et fonctionnement

Pour comprendre cette sécurité, il est essentiel de se familiariser avec le concept de mTLS (authentification TLS mutuelle). Avant cela, il est utile d’avoir une compréhension rapide de TLS.

TLS (Transport Layer Security) est un protocole standard couramment utilisé pour chiffrer les communications sur Internet, assurant ainsi la confidentialité et l’intégrité des données transmises entre le client (comme un navigateur web) et le serveur.

Le site de Cloudflare propose une illustration qui explique clairement son fonctionnement :

Fonctionnement TLS

Les étapes sont les suivantes :

  1. En premier lieu, le client se connecte au serveur.
  2. Puis, le serveur présente son certificat.
  3. Enfin, le client vérifie le certificat.
  4. Le client et le serveur échangent ensuite des informations via une connexion chiffrée.

Comme nous pouvons le voir, c’est uniquement le serveur qui présente son certificat.

Dans le cadre de mTLS, le principe reste similaire, mais cette fois, les deux parties (le client et le serveur) possèdent chacun un certificat et l’utilisent pour s’authentifier mutuellement.

Ainsi, le serveur prouve son identité au client et inversement. L’objectif est donc de garantir une authentification sécurisée et bilatérale.

Fonctionnement mTLS

Ainsi, après l’étape 3 (négociation de la clé de session), trois autres étapes sont nécessaires avant que la communication puisse réellement commencer :

  1. À son tour, le client présente son certificat.
  2. Puis, le serveur vérifie le certificat du client.
  3. Enfin, le serveur accorde l’accès.
  4. Le client et le serveur échangent ensuite des informations via une connexion chiffrée.

Configuration mTLS dans Cloudflare

Dans le cadre de Cloudflare, un certificat est utilisé pour prouver son identité au serveur d’origine. Ce dernier doit également disposer d’un certificat, tel que ceux délivrés par Let’s Encrypt ou Cloudflare.

Un échange légitime se déroule donc comme suit :

  • Lorsqu’un client accède au site « vaadata.example« , la requête est interceptée et vérifiée par Cloudflare.
  • Ensuite, Cloudflare envoie la requête au serveur d’origine tout en s’authentifiant avec son certificat. De cette manière, le serveur peut être certain que la requête provient bien de Cloudflare. Si le certificat est reconnu comme valide, le serveur accepte la communication.

Dans le cas d’un attaquant tentant d’accéder directement au site via l’adresse IP du serveur, l’accès échoue car il ne fournit pas de certificat valide pour s’authentifier. De même, s’il essaie de passer par le site « hacker.example » sans avoir correctement configuré les paramètres SSL pour utiliser un certificat Cloudflare valide, la connexion sera également rejetée par le serveur.

Cependant, l’attaquant peut reproduire la configuration du site « vaadata.example » car aucune donnée n’est indevinable. Pour ce faire, il lui suffit de suivre les étapes suivantes :

D’abord configurer SSL/TLS en full.

Puis activer l’option Authenticated Origin Pulls sur son domaine Cloudflare. Voici comment cela fonctionne :

Après ces deux étapes, la requête fonctionne à nouveau :

GET / HTTP/1.1
Host: hacker.example 
[…TRUNCATED HEADER…]

Réponse :

HTTP/1.1 200 OK
Host: hacker.example
[…TRUNCATED HEADER…]

<!doctype html>
<html lang="fr">
<head>
    <meta charset="utf-8">
    <title>Titre de la page</title>
</head>
<body>
<p>Hello world</p>
</body>
</html>

Dans notre cas cela fonctionne car par défaut, Cloudflare utilise le même certificat pour s’authentifier auprès des sites. Ainsi pour « vaadata.example » ou « hacker.example » le certificat fourni au serveur d’origine sera signé par la même autorité de certification

Cela signifie que tant que le serveur d’origine accepte les certificats Cloudflare par défaut, l’attaquant peut se faire passer pour un utilisateur légitime de Cloudflare juste en éditant son compte.

Pour se protéger contre ce type d’attaque, il est judicieux d’utiliser un certificat personnalisé, généré spécifiquement pour votre domaine, plutôt que d’utiliser le certificat par défaut de Cloudflare.

Une solution recommandée est la création d’un tunnel Cloudflare. Cette approche offre de nombreux avantages, notamment en renforçant la sécurité du serveur d’origine.

Avec un tunnel Cloudflare, toutes les connexions sont uniquement sortantes (outbound), ce qui permet de bloquer tout le trafic entrant vers le serveur d’origine. Cela réduit considérablement l’exposition du serveur aux attaques potentielles, car les requêtes ne peuvent atteindre le serveur que via le tunnel sécurisé établi par Cloudflare.

Ainsi, même si un attaquant connaît l’adresse IP du serveur, il ne pourra jamais y accéder si le serveur est correctement configuré pour fonctionner uniquement avec un tunnel Cloudflare.

De la même manière, toute tentative de l’attaquant de configurer un domaine sur Cloudflare pour imiter « vaadata.example » sera vaine, car il ne pourra jamais reproduire exactement la même configuration, c’est à dire associer le domaine au même tunnel.

De plus, même si l’attaquant parvient à établir une configuration similaire, Cloudflare effectue des contrôles de droits supplémentaires pour s’assurer que seules les requêtes provenant du domaine légitime passent par le tunnel sécurisé.

Ainsi, l’utilisation d’un tunnel Cloudflare offre une protection robuste contre les tentatives de contournement et les attaques directes sur le serveur d’origine.

Par ailleurs, l’utilisation d’un tunnel Cloudflare peut également être étendue à d’autres services; afin de garantir que tout le trafic passe par un canal sécurisé et contrôlé.

Le fonctionnement d’un tunnel Cloudflare est le suivant :

Fonctionnement tunnel Cloudflare

Selon nous, cette configuration est la meilleure solution pour sécuriser son serveur d’origine. Elle garantit que tout le trafic passe par un canal sécurisé et limite l’exposition du serveur aux attaques potentielles.

Limiter l’exposition des services pour contrer le risque d’attaques

Un point d’attention important dans la configuration de Cloudflare concerne les ports qu’il proxyfie. En effet, par défaut, Cloudflare fait le proxy de plusieurs ports, au-delà des ports standards 80 (HTTP) et 443 (HTTPS).

Ainsi, si votre serveur expose d’autres services sur des ports non sécurisés, comme le port 8080, et que la configuration n’est pas correctement restreinte, ces services risquent d’être involontairement exposés via Cloudflare.

Pour configurer cette protection au niveau de votre Cloudflare WAF, vous pouvez activer des règles spécifiques pour bloquer ou surveiller les requêtes vers des ports non standards.

  • Si vous utilisez les **WAF Managed Rules** (ancienne version), activez la règle `100015` (Anomaly:Port – Non Standard Port (not 80 or 443)) pour bloquer les connexions vers des ports autres que 80 et 443.
  • Si vous utilisez la nouvelle version (Cloudflare Web Application Firewall (WAF)), activez la règle `8e361ee4328f4a3caf6caf3e664ed6fe` (Anomaly:Port – Non Standard Port (not 80 or 443)) pour détecter et empêcher les accès à des ports non standards.

En appliquant ces règles, vous vous assurez que seules les connexions sur les ports autorisés (80 et 443) sont acceptées, réduisant ainsi le risque d’exposer des services non sécurisés ou inattendus via Cloudflare.

Conclusion

Nous avons exploré différentes configurations de Cloudflare pour sécuriser le serveur d’origine. Cependant, la plupart de ces configurations présentent des inconvénients qui peuvent permettre à un attaquant de contourner les protections.

L’approche des attaquants est souvent la même : tenter de reproduire la configuration de Cloudflare afin de l’utiliser comme vecteur d’attaque.

Bien que nous n’ayons pas approfondi les méthodes de contournement de Cloudflare, il est important de garder à l’esprit qu’elles existent. Cloudflare complique la tâche des attaquants, mais il n’est pas infaillible. Par conséquent, il doit être considéré comme une couche de défense en profondeur et non comme l’unique couche de protection.

SOURCES :

Images

https://www.cloudflare.com/resources/images/slt3lc6tev37/37w1tzGsD4XvYUkQCHbWG8/6fbbb48d0f5077cc2c662a4cc6817b1c/how_tls_works-what_is_mutual_tls.png

https://www.cloudflare.com/resources/images/slt3lc6tev37/5SjaQfZzDLEGqyzFkA0AA4/d227a26bbd7bc6d24363e9b9aaabef55/how_mtls_works-what_is_mutual_tls.png

https://developers.cloudflare.com/_astro/handshake.eh3a-Ml1_ZvgY0m.webp

Ressources clés

https://developers.cloudflare.com/fundamentals/basic-tasks/protect-your-origin-server

https://developers.cloudflare.com/fundamentals/reference/network-ports

https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/get-started/create-remote-tunnel

https://developers.cloudflare.com/cloudflare-one/connections/connect-networks

https://www.cloudflare.com/fr-fr/learning/access-management/what-is-mutual-tls

Auteur : Thomas DELFINO – Pentester @Vaadata