Le filtrage egress, le contrôle que tout le monde saute
Presque chaque pare-feu que j’audite est méticuleux sur ce qui peut entrer et indifférent à ce qui peut sortir. Cette asymétrie est exactement ce sur quoi compte un attaquant, parce que les coups qui comptent se jouent tous en sortie: le reverse shell appelle la maison, les données volées partent, l’implant balise vers son C2. Un pare-feu qui ne filtre que l’entrée est une porte à verrou solide sans mur derrière. Le but du filtrage egress, c’est qu’un hôte qui n’a rien à faire sur internet ne puisse pas y parler et qu’un hôte qui en a besoin n’atteigne que les quelques endroits dont il a vraiment besoin.
Pourquoi la sortie est la direction de l’attaquant
La raison pour laquelle l’exfiltration et le command-and-control passent par la sortie n’est pas subtile: une connexion que l’intérieur initie emprunte le chemin de retour qu’un pare-feu à états autorise déjà, de sorte qu’un implant n’a jamais à vaincre le périmètre, il lui suffit d’entamer la conversation depuis derrière. C’est pourquoi “on bloque l’entrée” ne protège de rien face à un hôte déjà compromis et pourquoi les canaux de choix sont ceux toujours permis en sortie, HTTPS vers n’importe où, DNS vers n’importe quel résolveur, parfois ICMP. Chacun est un tunnel si personne ne surveille la direction et la destination.
Default-deny, puis une liste d’autorisation
La posture est la même qui va de soi pour l’entrée et devient on ne sait comment polémique pour la sortie: refuser par défaut, permettre par exception. Sur un serveur, ce sont quelques lignes et les exceptions sont des destinations, pas seulement des ports:
iptables -P OUTPUT DROP
iptables -A OUTPUT -o lo -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# DNS interne seulement, et le proxy; rien d'autre ne sort directement
iptables -A OUTPUT -d 192.168.1.10 -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -d 192.168.1.100 -p tcp --dport 3128 -j ACCEPT
Une règle sans destination comme “autoriser le 443 vers n’importe où” n’est presque pas du filtrage egress, parce que chaque C2 moderne parle 443 vers n’importe où. La liste d’autorisation doit nommer l’endroit, qui pour un serveur est un ensemble court et connaissable: le miroir de paquets, la source de temps, le proxy interne, la seule API qu’il intègre.
La plupart des étages de back-end n’ont aucun besoin d’internet et le contrôle le plus propre pour ceux-là est de ne leur donner aucune route vers lui. Un serveur de base de données sans passerelle par défaut ne peut pas exfiltrer par un protocole que vous avez oublié de bloquer, parce qu’il n’a nulle part où envoyer le paquet:
ip route del default # l'étage atteint ses sous-réseaux, pas le monde
ip route add 10.0.0.0/8 via 192.168.1.1
Forcer DNS et HTTP par des points de passage
Deux protocoles demandent plus qu’une règle de port parce qu’ils sont les tunnels favoris.
Le DNS ne doit résoudre que par votre résolveur et le DNS direct vers tout autre, y compris les points DoH chiffrés qui laissent un hôte contourner entièrement votre résolveur, doit être jeté:
iptables -A OUTPUT -p udp --dport 53 ! -d 192.168.1.10 -j DROP
iptables -A OUTPUT -p tcp --dport 53 ! -d 192.168.1.10 -j DROP
iptables -A OUTPUT -d 1.1.1.1 -p tcp --dport 443 -j DROP # DoH public connu
iptables -A OUTPUT -d 8.8.8.8 -p tcp --dport 443 -j DROP
Le trafic web passe par un proxy sortant qui liste les destinations autorisées, de sorte que la règle de pare-feu est “atteindre le proxy” et que la politique vit là où elle peut voir des URL plutôt que de simples IP:
# squid: autoriser une liste nommée, refuser le reste
acl allowed dstdomain "/etc/squid/allowed-sites.txt"
http_access allow allowed
http_access deny all
Le proxy est aussi là où l’egress trouve ses logs: un enregistrement central de chaque destination que chaque hôte a tenté d’atteindre, c’est-à-dire l’artefact le plus utile quand quelque chose tourne mal.
La détection, honnêtement
Le filtrage egress est le contrôle; la surveillance est la façon d’apprendre qu’il fonctionne et où il est trop serré ou trop lâche. Les signaux qui comptent sont un hôte qui atteint une destination hors de sa liste, une balise régulière à faible volume (la même petite requête toutes les N minutes est du C2, pas un utilisateur), un POST ou un envoi gros et soutenu depuis un étage qui ne devrait que servir et la forme du tunneling DNS traitée pour elle-même, labels longs à haute entropie et flux de sous-domaines uniques. Les enregistrements de flux ou le log du proxy portent tout cela; le DROP log du pare-feu sur la chaîne OUTPUT en est la version la moins chère et vous dit immédiatement quel hôte a tenté d’aller là où il ne devait pas.
Ce contre quoi je mettrais en garde, c’est de saisir des massues côté hôte pour compenser un contrôle réseau absent.
Désactiver file_get_contents de PHP, par exemple, casse une large part des applications réelles tout en ralentissant à peine un attaquant qui a d’autres voies de sortie; la frontière egress appartient au réseau, où elle s’applique à chaque processus de l’hôte quel que soit le langage.
Où atterrir
Mettez la politique OUTPUT à DROP et autorisez par destination nommée, pas par port nu. Donnez aux étages qui n’ont pas besoin d’internet aucune route par défaut, pour que la question ne se pose jamais. Forcez le DNS par votre résolveur et bloquez le contournement DoH, envoyez le trafic web par un proxy à liste d’autorisation et gardez les logs du proxy et les DROP du pare-feu parce qu’ils sont votre fil-piège d’exfiltration. Le filtrage egress demande plus de travail que l’entrée et attrape la moitié de l’attaque que l’entrée ne voit jamais: le moment où vos données ou votre shell, sont en route vers la sortie.
[edit: 2026] L’implémentation Ansible de tout ceci: le rôle nftables .