Aller au contenu
M08 · C. Protéger les applications Web

Le pare-feu applicatif et le développement sécurisé

Le pare-feu applicatif web (WAF) qui lit le contenu des requêtes HTTP - règles, ModSecurity et OWASP CRS, faux positifs, contournements -, puis les principes du développement sécurisé - entrées non fiables, moindre privilège, erreurs discrètes, secrets hors du code, dépendances à jour - et les en-têtes de sécurité HTTP.

1Situer

Le pare-feu réseau de la leçon précédente laisse passer le port 443, et il le doit : c'est par là qu'arrivent les clients. Mais c'est aussi par là qu'arrivent les injections SQL et le XSS. Le pare-feu applicatif ouvre chaque requête HTTP pour en lire le contenu. Il aide, mais la vraie protection reste un code écrit avec de bonnes habitudes : c'est le développement sécurisé, qui termine ce module.

2Comprendre

Le pare-feu applicatif lit le contenu des requêtes
?q=savon Pare-feuréseau : 443 ✔ WAFcontenu HTTP PHPprepare(…) MySQL Savon noir : 30 DH 403 texte cherché, rien ne fuit

Une recherche normale, q=savon, arrive sur le port 443 : le pare-feu réseau la laisse passer.

1 / 5

Le pare-feu applicatif

Le WAF prend place juste après le pare-feu réseau :

Internet → pare-feu réseau (adresses, ports) → WAF (contenu HTTP) → serveur web → PHP → MySQL

Il existe sous plusieurs formes :

  • un module du serveur web : ModSecurity pour Apache ou Nginx, le plus connu des WAF libres ;
  • un service en ligne placé devant le site, souvent fourni avec le CDN (leçon A.2, DDoS) ;
  • une couche dans l’application elle-même, comme le petit waf.php de la simulation.

Les limites du WAF

Les principes du développement sécurisé

Les règles que vous avez pratiquées tout au long des modules 07 et 08 :

PrincipeDans le code
Ne jamais faire confiance aux entréesValider côté serveur ce qui vient de $_GET, $_POST, $_COOKIE, des fichiers et des en-têtes
Séparer les données du codeRequêtes préparées (SQL), htmlspecialchars (HTML), textContent (JavaScript)
Moindre privilègeUn compte MySQL du site sans DROP ni GRANT, des rôles vérifiés à chaque page
Défense en profondeurPare-feu réseau, WAF, code sûr, base chiffrée : plusieurs couches indépendantes
Échouer en sécuritéEn cas de doute, refuser. Une erreur affiche une page neutre et se lit dans le journal
Secrets hors du codeMots de passe et clés dans un fichier .env hors du dossier public et hors de Git
Dépendances à jourcomposer audit et npm audit régulièrement, mises à jour de PHP et du framework
JournaliserConnexions, échecs, actions d’administration, sans jamais écrire de mot de passe

Les en-têtes de sécurité HTTP

En-têteCe qu’il demande au navigateur
Content-Security-Policy: default-src 'self'N’exécuter que les scripts et styles venus du site lui-même (limite le XSS, leçon A.2)
Strict-Transport-Security: max-age=31536000Toujours utiliser HTTPS pour ce site pendant un an, même si l’on tape http://
X-Content-Type-Options: nosniffRespecter le type annoncé d’un fichier : un « texte » envoyé par un utilisateur n’est jamais exécuté comme script
X-Frame-Options: DENYInterdire l’affichage du site dans un cadre (iframe) d’un autre site, ce qui empêche le clickjacking
Referrer-Policy: strict-origin-when-cross-originNe pas transmettre l’adresse complète des pages aux autres sites

En PHP, on les envoie avant tout affichage, une fois pour toutes dans un fichier inclus partout :

<?php
header("Content-Security-Policy: default-src 'self'");
header('Strict-Transport-Security: max-age=31536000');
header('X-Content-Type-Options: nosniff');
header('X-Frame-Options: DENY');
header('Referrer-Policy: strict-origin-when-cross-origin');

C’est la dernière leçon du module : vous savez maintenant reconnaître les principales attaques, protéger les données et configurer les défenses d’une application web. Pour vérifier un site avant sa mise en ligne, la liste de l’exercice « Le bilan avant la mise en ligne » est un bon point de départ.

Exemple simple

Le contrôle des bagages à l'aéroport

Le contrôle des billets (pare-feu réseau) vérifie que vous allez au bon terminal. Le scanner des bagages (WAF) regarde ce que vous transportez. Les deux sont utiles, et aucun ne remplace l'autre.

Exemple simple

La recette écrite proprement

Un cuisinier qui se lave les mains, garde le poulet cru à part et vérifie les dates n'a pas besoin d'un inspecteur pour éviter l'intoxication. Le développement sécurisé, ce sont ces gestes répétés à chaque ligne de code.

3Manipuler

La recherche de Souk Atlas passe par un petit pare-feu applicatif (waf.php). Ses règles sont trop naïves : essayez les attaques proposées, puis améliorez les règles (expressions régulières insensibles à la casse) sans bloquer les recherches normales, et ajoutez deux en-têtes de sécurité.
Pas à pas dans le lab ↗ Cliquez sur Exécuter : PHP 8.3 démarre dans votre navigateur (environ 5 Mo à télécharger, une seule fois).

La page s'affichera ici.

Le navigateur demandera /recherche.php?q=savon au serveur, qui exécutera le PHP et renverra du HTML.

Entre le navigateur et le serveur aucun échange pour l'instant

Consignes 0 / 5

  1. La balise script en casse mélangée est bloquée (403)
  2. L'injection « UNION ALL SELECT » (deux espaces) est bloquée (403)
  3. Pas de faux positif : la recherche « Union des artisans » reste permise
  4. Chaque réponse envoie X-Content-Type-Options: nosniff
  5. Chaque réponse envoie X-Frame-Options: DENY

4Pratiquer

Exercice 1 · Réseau ou applicatif ?

Pour chaque attaque, dites quel pare-feu peut la repérer : (a) un robot qui tente le port 22 ; (b) un paramètre ?id=1 OR 1=1 ; (c) une adresse IP bannie qui revient ; (d) un champ de formulaire contenant <script>.

Voir le corrigé
(a) Pare-feu réseau : il voit le port 22 et le bloque.
(b) Pare-feu applicatif : le contenu de la requête HTTP (le paramètre id) n'est visible qu'en lisant HTTP.
(c) Les deux savent bloquer une adresse ; le pare-feu réseau le fait le plus tôt et le moins cher.
(d) Pare-feu applicatif. Et surtout le code : htmlspecialchars à l'affichage.

Exercice 2 · Une règle de WAF

Écrivez une règle (expression régulière PHP) qui détecte une tentative de traversée de répertoires (../ ou ..\, y compris encodée %2e%2e%2f) dans une valeur déjà décodée par PHP.

Voir un indice

PHP décode déjà %2e%2e%2f en ../ dans $_GET. Pensez aux deux barres, / et \.

Voir le corrigé
<?php
$REGLES[] = ['traversée de répertoires', '#\.\.[/\\\\]#'];
// Mais la vraie protection reste dans le code : basename() et une liste de fichiers permis (leçon A.2).

Exercice 3 · Le bilan avant la mise en ligne

Votre projet de fin de module va être mis en ligne. Listez six vérifications de sécurité à faire avant.

Voir le corrigé
1. display_errors = Off en production, les erreurs vont dans un journal.
2. Les secrets (mot de passe MySQL, clés d'API) sont dans un fichier .env hors du dossier public et hors de Git.
3. Toutes les requêtes SQL sont préparées, toutes les sorties échappées.
4. Les mots de passe sont hachés avec password_hash, la connexion limite les tentatives.
5. HTTPS partout, avec les en-têtes de sécurité (HSTS, CSP, nosniff, X-Frame-Options) et des cookies Secure, HttpOnly, SameSite.
6. composer audit (ou npm audit) ne signale aucune dépendance vulnérable ; le compte MySQL du site n'a que les droits nécessaires.

5Vérifier

1. Que lit un pare-feu applicatif (WAF) qu'un pare-feu réseau ne lit pas ?
2. Le site a un WAF. Peut-on se passer des requêtes préparées ?
3. Qu'est-ce qu'un faux positif pour un WAF ?
4. Pourquoi désactiver display_errors en production ?