En bref : un pare-feu pour serveur web n'autorise en entrée que TCP 80 et 443, avec HTTP/3 en plus UDP 443, et SSH uniquement depuis des réseaux connus. Tout le reste est rejeté : bases de données, caches, PHP-FPM et interfaces d'administration n'ont pas leur place sur Internet. Vérifiez d'abord avec ss -tulpn ce qui écoute, construisez ensuite un jeu de règles avec la valeur par défaut « rejeter » et testez le résultat de l'extérieur. Limiter les connexions sortantes restreint en outre les conséquences d'une intrusion.
Les portes oubliées à côté de la porte d'entrée
Avec PHP2Shell, le regard s'est porté sur le serveur web lui-même, sur les ports 80 et 443. Ceux-là doivent être ouverts, sinon il n'y a pas de site web. Lors des états des lieux après de telles failles, on remarque pourtant souvent autre chose : à côté de la porte d'entrée, d'autres portes sont ouvertes. Une base de données écoute sur toutes les interfaces réseau, un pool PHP-FPM sur le port 9000, un service de cache sans mot de passe, une interface d'administration de la phase de mise en place.
Chacune de ces portes est une surface d'attaque à part entière, avec ses propres failles. Un pare-feu qui n'autorise que le nécessaire les ferme avec quelques règles. C'est l'une des mesures les plus efficaces pour sécuriser son propre serveur, comme le décrit aussi la page Sécuriser soi-même serveurs dédiés et vServers.
Ce qui doit être ouvert, ce qui ne doit pas l'être
Le principe est une liste d'autorisation : tout est fermé, et seuls les services délibérément autorisés sont accessibles. L'aperçu suivant aide à classer.
| Service | Port typique | Accessible de l'extérieur ? |
|---|---|---|
| HTTP et HTTPS | TCP 80, TCP 443 | oui |
| HTTP/3 (QUIC) | UDP 443 | seulement s'il est utilisé |
| SSH | TCP 22 | uniquement depuis des réseaux connus ou par VPN |
| Panneau d'hébergement | selon le produit | uniquement depuis des réseaux connus |
| MySQL/MariaDB, PostgreSQL | TCP 3306, TCP 5432 | non |
| Redis, Memcached | TCP 6379, TCP 11211 | non |
| PHP-FPM | TCP 9000 ou socket Unix | non, de préférence uniquement le socket |
| Serveur de messagerie | TCP 25, 465, 587, 993 | seulement si le serveur reçoit ou met à disposition des e-mails |
Un service qui n'a pas besoin d'être joignable de l'extérieur ne devrait en outre écouter que sur 127.0.0.1 ou un socket Unix. Il reste alors inaccessible même en cas de panne du pare-feu. Pour les accès d'administration, des autorisations par IP ou un VPN conviennent ; les détails figurent dans l'article Protéger les zones d'administration par IP, VPN ou protection supplémentaire.
Étape 1 : déterminer ce qui écoute
Avant d'écrire des règles, faites le point. Les commandes suivantes ne font que lire :
# Tous les ports TCP et UDP en écoute avec le processus
sudo ss -tulpn
# Jeu de règles actuel de nftables
sudo nft list ruleset
# État d'ufw, s'il est utilisé
sudo ufw status verbose
Dans la sortie de ss, soyez attentif à l'adresse devant le port. 0.0.0.0 ou [::] signifie : le service écoute sur toutes les interfaces. 127.0.0.1 signifie : uniquement en local. Notez pour chaque service s'il doit être joignable de l'extérieur. Cette liste fait aussi partie de votre inventaire de serveurs.
Pare-feu pour serveur web avec nftables
nftables est le filtre de paquets actuel sous Linux. Le jeu de règles suivant s'inspire de l'exemple du wiki officiel de nftables et ajoute une autorisation pour SSH uniquement depuis un réseau d'administration. Les adresses sont des exemples de documentation et doivent être remplacées.
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
set verwaltung_v4 {
type ipv4_addr
flags interval
elements = { 203.0.113.0/28 }
}
chain input {
type filter hook input priority 0; policy drop;
ct state vmap { established : accept, related : accept, invalid : drop }
iifname lo accept
# IPv6 a besoin de la découverte de voisins
icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
icmp type echo-request limit rate 5/second accept
icmpv6 type echo-request limit rate 5/second accept
# Site web
tcp dport { 80, 443 } accept
# uniquement avec HTTP/3 :
# udp dport 443 accept
# SSH uniquement depuis le réseau d'administration
ip saddr @verwaltung_v4 tcp dport 22 accept
}
chain forward {
type filter hook forward priority 0; policy drop;
}
}
Vérifiez le jeu de règles avant de le charger avec nft -c -f /etc/nftables.conf. La première ligne flush ruleset supprime toutes les règles existantes, y compris celles que Docker ou Fail2ban ont inscrites. Sur les serveurs avec Docker ou d'autres services qui posent leurs propres règles, adaptez donc le jeu de règles au lieu de le remplacer globalement. La manière dont Fail2ban coopère avec le pare-feu est décrite dans Configurer Fail2ban pour les serveurs web.
Qui préfère plus simple utilise ufw sous Ubuntu. L'ordre est important : d'abord l'autorisation SSH, puis l'activation.
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow proto tcp from 203.0.113.0/28 to any port 22
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose
Ne modifiez les règles de pare-feu sur des serveurs distants qu'avec une seconde voie d'accès, par exemple la console de votre fournisseur. Une mauvaise règle SSH vous bloquerait sinon vous-même.
Connexions sortantes : la moitié sous-estimée
La plupart des pare-feu autorisent tout en sortie. Pour un serveur web, c'est généreux. Après une intrusion, les attaquants utilisent les connexions sortantes pour recharger des outils, exfiltrer des données ou recevoir des commandes. Les processus du serveur web ou de PHP qui établissent des connexions inattendues sont donc considérés comme un signal d'alerte.
Limiter les connexions sortantes est plus laborieux, car chaque connexion légitime doit être connue : sources de paquets pour les mises à jour, services de paiement, interfaces, envoi d'e-mails, serveurs de temps. Une voie pragmatique consiste à d'abord journaliser les connexions sortantes, puis à constituer une liste d'autorisation et seulement ensuite à bloquer. La manière de vérifier les connexions existantes est décrite dans Vérifier les connexions sortantes d'un serveur ; les autres traces qui indiquent une intrusion figurent dans Suis-je déjà compromis ?.
Pour finir, testez de l'extérieur, depuis un autre ordinateur et uniquement contre vos propres systèmes, quels ports sont réellement accessibles. Seul ce test montre si Docker, le pare-feu du fournisseur et les règles du serveur donnent ensemble l'image souhaitée.
Conclusion : peu de règles, grand effet
Un pare-feu de serveur web n'a pas besoin d'être compliqué. Ports web ouverts, administration uniquement depuis des réseaux connus, tout le reste rejeté : avec ces règles de base, les bases de données, les caches et les services oubliés disparaissent d'Internet. C'est l'une des mesures au meilleur rapport entre effort et utilité.
Contre les failles du serveur web ou de PHP lui-même, le pare-feu n'aide pas, car ce sont précisément ces ports qui doivent rester ouverts. Sa force est de maintenir le reste de la surface d'attaque réduit et, avec une limitation des connexions sortantes, de contenir les conséquences d'une intrusion.
L'entretien est décisif. Chaque nouveau service, chaque conteneur et chaque changement chez le fournisseur peut déplacer le tableau. Répétez donc la vérification de l'extérieur après chaque changement important et à intervalles fixes, par exemple une fois par trimestre.
Comment PHP SOS aide-t-il concrètement ?
- Faire l'inventaire : nous recensons tous les services en écoute et vérifions de l'extérieur ce qui est réellement accessible.
- Construire le jeu de règles : nous établissons une liste d'autorisation pour les connexions entrantes et, là où c'est judicieux, sortantes, en accord avec Docker, Fail2ban et le pare-feu du fournisseur.
- Basculer en sécurité : nous réalisons les modifications avec une seconde voie d'accès et un plan de repli.
- Documenter : nous consignons ports, autorisations et motifs dans le cadre du durcissement de serveur.
Questions fréquentes
De quels ports un serveur web a-t-il besoin ?
Pour les visiteurs, en règle générale uniquement TCP 80 et 443, avec HTTP/3 en plus UDP 443. SSH et les interfaces d'administration ne devraient être accessibles que depuis des réseaux connus ou via VPN. Les ports de bases de données, de cache et de PHP-FPM n'ont pas leur place sur Internet.
Un pare-feu protège-t-il contre PHP2Shell ?
Non. PHP2Shell est exploité via le serveur web, donc via précisément les ports qui doivent être ouverts. Mais le pare-feu réduit le reste de la surface d'attaque, et une limitation des connexions sortantes peut contenir les conséquences d'une intrusion.
Pourquoi ufw affiche-t-il comme fermés des ports qui restent pourtant accessibles ?
Un cas connu est celui des conteneurs Docker : Docker inscrit les ports publiés directement dans les règles de filtrage de paquets, en contournant ufw. Vérifiez donc toujours en plus les ports ouverts depuis l'extérieur.
Le pare-feu de mon hébergeur suffit-il ?
Un pare-feu en amont chez le fournisseur est une bonne deuxième couche. Il ne remplace toutefois pas le pare-feu du serveur, car il n'agit pas en cas d'erreur dans la configuration du fournisseur ni pour les connexions internes. Le mieux est d'utiliser les deux avec les mêmes règles de base.