<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>arch.log | Corentin BRINGER | Section blog</title><description/><link>https://blog.corentinbringer.fr/</link><language>fr</language><item><title>Monter un faux portail captif sur Raspberry Pi pour sensibiliser au Wi-Fi public</title><link>https://blog.corentinbringer.fr/blog/faux-portail-captif-raspberry-pi/</link><guid isPermaLink="true">https://blog.corentinbringer.fr/blog/faux-portail-captif-raspberry-pi/</guid><description>Un Raspberry Pi caché dans un faux plafond, un point d&apos;accès Wi-Fi ouvert et un portail captif aux couleurs de l&apos;entreprise suffisent à collecter des identifiants Active Directory. Récit d&apos;un montage purple team, de la carte SD au proxy transparent.</description><pubDate>Wed, 15 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Ce projet part d’une idée simple : un collaborateur qui se connecte à un Wi-Fi ouvert au nom de son entreprise ne se méfie pas. En reproduisant ce scénario dans un cadre maîtrisé, on obtient un support de sensibilisation concret plutôt qu’une slide de plus sur le hameçonnage.&lt;/p&gt;
&lt;p&gt;La démarche relève de la &lt;strong&gt;purple team&lt;/strong&gt;. Il ne s’agit pas d’une équipe distincte mais d’un mode de collaboration : utiliser des techniques offensives (red) dans le but immédiat d’améliorer les défenses (blue). Simuler une attaque de portail captif pour former ses collègues, c’est exactement cela.&lt;/p&gt;
&lt;aside aria-label=&quot;Cadre légal et éthique&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Cadre légal et éthique&lt;/p&gt;&lt;div&gt;&lt;p&gt;Intercepter des communications et collecter des identifiants sur un réseau, même interne, exige une autorisation écrite de la direction et un périmètre défini à l’avance. Ce montage ne se justifie que dans une mission de sensibilisation validée, avec information des personnes concernées en aval et purge des données collectées. Hors de ce cadre, la collecte d’identifiants est un délit.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;h2 id=&quot;préparer-la-carte-et-los&quot;&gt;Préparer la carte et l’OS&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Le point de départ tient en trois éléments : un Raspberry Pi (Pi 3, 4 ou 5), une carte MicroSD et une alimentation correctement dimensionnée.&lt;/p&gt;
&lt;p&gt;L’installation passe par &lt;strong&gt;Raspberry Pi Imager&lt;/strong&gt;. L’outil télécharge et écrit &lt;strong&gt;Raspberry Pi OS&lt;/strong&gt; sur la carte, une distribution basée sur Debian. L’assistant permet de préconfigurer le nom d’hôte, l’utilisateur, le mot de passe et surtout d’activer SSH avant le premier démarrage, ce qui évite d’avoir à brancher un écran.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;premier-démarrage--lire-les-led&quot;&gt;Premier démarrage : lire les LED&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Une fois la carte insérée et l’alimentation branchée, le Pi communique son état par deux LED : une rouge (&lt;code dir=&quot;auto&quot;&gt;PWR&lt;/code&gt;, alimentation) et une verte (&lt;code dir=&quot;auto&quot;&gt;ACT&lt;/code&gt;, activité).&lt;/p&gt;
&lt;p&gt;En démarrage normal :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LED rouge fixe&lt;/strong&gt; : le Pi reçoit une tension correcte en 5 V. Si elle clignote ou s’éteint, le câble ou le bloc d’alimentation manque de puissance.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;LED verte au clignotement irrégulier&lt;/strong&gt; : le Pi lit et écrit sur la carte MicroSD, il démarre. Une fois le boot terminé, elle s’éteint ou clignote très discrètement.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Quand le Pi refuse de démarrer, la LED verte adopte un motif régulier qui code l’erreur :&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Clignotements LED verte&lt;/th&gt;
&lt;th&gt;Signification&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Fichier de boot (&lt;code dir=&quot;auto&quot;&gt;start*.elf&lt;/code&gt;) introuvable : carte mal flashée ou vide&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;Image de l’OS non supportée, par exemple noyau introuvable&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fixe, sans clignoter&lt;/td&gt;
&lt;td&gt;Le Pi ne lit pas la carte SD : mal insérée ou morte&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Si la LED rouge reste fixe et que la verte cesse son activité après trente à quarante secondes, le Pi a démarré et attend sur le réseau.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;trouver-le-pi-et-sy-connecter&quot;&gt;Trouver le Pi et s’y connecter&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Sans écran, il faut retrouver l’adresse IP du Pi sur le réseau. Trois approches :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Balayer le réseau pour lister les hôtes actifs&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nmap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-sP&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;172.30.11.0/24&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Ne garder que les hôtes dont le port SSH est ouvert&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nmap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;22&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;172.30.13.0/24&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--open&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Résolution mDNS si le nom d&apos;hôte est connu&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ping&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&amp;#x3C;hostname&gt;.local&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;La connexion se fait ensuite en SSH :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ssh&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;user@IP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Et parce que taper &lt;code dir=&quot;auto&quot;&gt;ls -la&lt;/code&gt; en entier vingt fois par session, très peu pour moi, un petit alias par pure flemme :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;alias ll=&apos;ls -la&apos;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&gt;&gt;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.bashrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.bashrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;servir-le-portail-avec-nginx-et-php&quot;&gt;Servir le portail avec Nginx et PHP&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Le portail captif est une page web hébergée sur le Pi. Nginx la sert, PHP-FPM traite le formulaire de connexion.&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nginx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;php-fpm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Les fichiers du portail (la page de connexion et ses assets) se placent dans &lt;code dir=&quot;auto&quot;&gt;/var/www/html/&lt;/code&gt;. Ils sont disponibles dans le dépôt du projet : &lt;a href=&quot;https://github.com/Pitchouneee/wifi-evil-twin/tree/main/captive_portal&quot;&gt;github.com/Pitchouneee/wifi-evil-twin/tree/main/captive_portal&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;Page du faux portail captif aux couleurs de l’entreprise, avec un formulaire d’identifiant Active Directory&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1680&quot; height=&quot;949&quot; src=&quot;https://blog.corentinbringer.fr/_astro/captive-portal.COiO336p_1Ycw0l.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Pour que Nginx exécute les fichiers PHP, il faut modifier le bloc serveur par défaut dans &lt;code dir=&quot;auto&quot;&gt;/etc/nginx/sites-available/default&lt;/code&gt;. La configuration finale ajoute &lt;code dir=&quot;auto&quot;&gt;index.php&lt;/code&gt;, active le traitement PHP et intercepte les URL de détection de portail des systèmes mobiles (le rôle de ces dernières est détaillé plus bas) :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;server&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;listen &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;default_server&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;listen &lt;/span&gt;&lt;span&gt;[::]:80 &lt;/span&gt;&lt;span&gt;default_server&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;root &lt;/span&gt;&lt;span&gt;/var/www/html;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;index &lt;/span&gt;&lt;span&gt;index.html index.htm index.php;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;   &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;server_name &lt;/span&gt;&lt;span&gt;_;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; / {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;try_files &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt; $&lt;/span&gt;&lt;span&gt;uri&lt;/span&gt;&lt;span&gt;/ &lt;/span&gt;&lt;span&gt;=404&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Rediriger les URL de test de connectivité vers le portail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/(hotspot-detect\.html|canonical\.html|success\.txt) &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;302&lt;/span&gt;&lt;span&gt; http://10.42.0.1/index.php;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~*&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/(generate_204|gen_204) &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;        &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;302&lt;/span&gt;&lt;span&gt; http://10.42.0.1/index.php;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;# Traitement PHP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;location&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;\.php$ &lt;/span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;include &lt;/span&gt;&lt;span&gt;snippets/fastcgi-php.conf;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;       &lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;fastcgi_pass &lt;/span&gt;&lt;span&gt;unix:/var/run/php/php8.4-fpm.sock;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Le chemin de la socket doit correspondre à la version de PHP installée, ici &lt;code dir=&quot;auto&quot;&gt;php8.4-fpm.sock&lt;/code&gt;. Avant de recharger, une vérification de syntaxe évite les mauvaises surprises :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nginx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nginx&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Les fichiers du portail doivent appartenir à l’utilisateur du serveur web pour que PHP puisse les lire et écrire son journal :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-R&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;www-data:www-data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/www/html/&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;créer-le-point-daccès-wi-fi&quot;&gt;Créer le point d’accès Wi-Fi&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Le Pi doit maintenant diffuser son propre réseau Wi-Fi. NetworkManager gère cela nativement via &lt;code dir=&quot;auto&quot;&gt;nmcli&lt;/code&gt;, sans avoir à assembler manuellement &lt;code dir=&quot;auto&quot;&gt;hostapd&lt;/code&gt; et &lt;code dir=&quot;auto&quot;&gt;dnsmasq&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On vérifie d’abord que la carte Wi-Fi est reconnue :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;nmcli&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Puis on crée le point d’accès. Le nom du réseau (&lt;code dir=&quot;auto&quot;&gt;ssid&lt;/code&gt;) reprend celui de l’entreprise pour paraître légitime :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 1. Créer la connexion Wi-Fi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;nmcli&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connection&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;type&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wifi&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ifname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wlan0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;con-name&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyHotspot&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;autoconnect&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;yes&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ssid&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;My_Wifi&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 2. Passer en mode point d&apos;accès et partager la connexion&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;nmcli&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connection&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;modify&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyHotspot&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;802-11-wireless.mode&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;802-11-wireless.band&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;bg&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ipv4.method&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;shared&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 3. Activer le hotspot&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;nmcli&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;connection&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;up&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;MyHotspot&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;L’option &lt;code dir=&quot;auto&quot;&gt;ipv4.method shared&lt;/code&gt; fait du Pi un routeur : il attribue les adresses en DHCP, résout le DNS et fait du NAT vers Internet. NetworkManager choisit une passerelle locale, généralement &lt;code dir=&quot;auto&quot;&gt;10.42.0.1&lt;/code&gt;. On la récupère ainsi :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;hostname&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-I&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# ou&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;show&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wlan0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Toutes les commandes qui suivent utilisent &lt;code dir=&quot;auto&quot;&gt;10.42.0.1&lt;/code&gt;. Cette adresse dépend de la configuration du point d’accès, d’où les valeurs différentes que l’on croise ailleurs (&lt;code dir=&quot;auto&quot;&gt;10.3.141.1&lt;/code&gt; avec d’autres piles logicielles). Il faut l’adapter à ce que renvoie &lt;code dir=&quot;auto&quot;&gt;hostname -I&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;intercepter-le-trafic-http&quot;&gt;Intercepter le trafic HTTP&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Pour qu’une requête HTTP quelconque aboutisse sur Nginx, on redirige le trafic sortant du Wi-Fi vers le serveur local avec &lt;code dir=&quot;auto&quot;&gt;iptables&lt;/code&gt; :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;iptables&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PREROUTING&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wlan0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tcp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-j&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DNAT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--to-destination&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10.42.0.1:80&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Sur le papier, tout terminal connecté au Wi-Fi qui charge une page en HTTP est renvoyé vers le portail. En pratique, la première tentative sur iPhone n’a rien ouvert du tout.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;déclencher-louverture-automatique-du-portail&quot;&gt;Déclencher l’ouverture automatique du portail&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;À la connexion à un réseau Wi-Fi, iOS interroge en arrière-plan une page de contrôle : &lt;code dir=&quot;auto&quot;&gt;http://captive.apple.com/hotspot-detect.html&lt;/code&gt;. Si la réponse est un &lt;code dir=&quot;auto&quot;&gt;200 Success&lt;/code&gt;, le téléphone considère qu’Internet est ouvert. Toute autre réponse lui indique la présence d’un portail, et il ouvre alors la fenêtre dédiée, le &lt;strong&gt;CNA&lt;/strong&gt; (Captive Network Assistant). Android procède de même avec &lt;code dir=&quot;auto&quot;&gt;generate_204&lt;/code&gt; et &lt;code dir=&quot;auto&quot;&gt;gen_204&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Lors du test, le portail ne s’ouvrait pas automatiquement. Pour vérifier que l’interception fonctionnait malgré tout, direction le navigateur et &lt;code dir=&quot;auto&quot;&gt;http://neverssl.com/&lt;/code&gt;, un site volontairement servi en HTTP simple. Le portail s’est bien affiché, mais seulement en le sollicitant manuellement.&lt;/p&gt;
&lt;p&gt;Si l’iPhone charge le vrai &lt;code dir=&quot;auto&quot;&gt;neverssl.com&lt;/code&gt; sans redirection, c’est que le trafic traverse le Pi vers Internet sans être intercepté. Deux causes possibles : la règle &lt;code dir=&quot;auto&quot;&gt;iptables&lt;/code&gt; a sauté, ou le trafic emprunte une autre route.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;le-piège-iptables--une-règle-accept-qui-court-circuite-le-dnat&quot;&gt;Le piège iptables : une règle ACCEPT qui court-circuite le DNAT&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;L’inspection de la table &lt;code dir=&quot;auto&quot;&gt;nat&lt;/code&gt; a livré la réponse :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;iptables&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-L&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PREROUTING&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;549  131K ACCEPT     all  --  *  *  10.42.0.149          0.0.0.0/0&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;L’iPhone avait obtenu l’adresse &lt;code dir=&quot;auto&quot;&gt;10.42.0.149&lt;/code&gt; par le DHCP du hotspot. La toute première règle de la chaîne disait à &lt;code dir=&quot;auto&quot;&gt;iptables&lt;/code&gt; : « si un paquet vient de &lt;code dir=&quot;auto&quot;&gt;10.42.0.149&lt;/code&gt;, &lt;code dir=&quot;auto&quot;&gt;ACCEPT&lt;/code&gt;, laisse-le passer directement sans évaluer la suite ». Comme les règles sont lues de haut en bas, le téléphone correspondait à cette règle immédiatement. La règle de redirection &lt;code dir=&quot;auto&quot;&gt;DNAT&lt;/code&gt;, placée tout en bas, n’était jamais atteinte. Le téléphone accédait au vrai site et croyait Internet ouvert, ce qui bloquait l’affichage automatique du portail.&lt;/p&gt;
&lt;p&gt;La correction tient en deux temps. D’abord supprimer la règle d’autorisation qui court-circuite tout :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;iptables&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-D&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PREROUTING&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10.42.0.149&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-j&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ACCEPT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Ensuite insérer la règle de redirection en tête de chaîne avec &lt;code dir=&quot;auto&quot;&gt;-I&lt;/code&gt; (insert) plutôt que &lt;code dir=&quot;auto&quot;&gt;-A&lt;/code&gt; (append), pour qu’aucun &lt;code dir=&quot;auto&quot;&gt;ACCEPT&lt;/code&gt; injecté par un autre service ne repasse devant :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;iptables&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-I&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PREROUTING&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wlan0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tcp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-j&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DNAT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--to-destination&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10.42.0.1:80&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;À partir de là, le CNA de l’iPhone s’ouvre seul dès la connexion.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;libérer-lutilisateur-après-la-saisie&quot;&gt;Libérer l’utilisateur après la saisie&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Le portail présente un formulaire aux couleurs de l’entreprise qui demande un identifiant Active Directory. Le script &lt;code dir=&quot;auto&quot;&gt;connect.php&lt;/code&gt; reçoit la soumission, journalise l’identifiant, puis « libère » l’utilisateur en ajoutant une règle &lt;code dir=&quot;auto&quot;&gt;ACCEPT&lt;/code&gt; pour son adresse IP afin qu’il retrouve un accès normal.&lt;/p&gt;
&lt;p&gt;Ce script tourne sous l’utilisateur &lt;code dir=&quot;auto&quot;&gt;www-data&lt;/code&gt;, qui n’a pas le droit de modifier &lt;code dir=&quot;auto&quot;&gt;iptables&lt;/code&gt;. On l’autorise pour cette seule commande, sans mot de passe, via &lt;code dir=&quot;auto&quot;&gt;sudo visudo&lt;/code&gt; :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;www-data ALL=(ALL) NOPASSWD: /usr/sbin/iptables -t nat -I PREROUTING -s * -j ACCEPT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Le script lui-même :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&amp;#x3C;?&lt;/span&gt;&lt;span&gt;php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;$_SERVER&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;REQUEST_METHOD&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;===&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;POST&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;$username&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;isset&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$_POST&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]) &lt;/span&gt;&lt;span&gt;?&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;trim&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;$_POST&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;]) &lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Inconnu&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;$user_ip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$_SERVER&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;REMOTE_ADDR&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;$date&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;Y-m-d H:i:s&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;$logEntry&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sprintf&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;[%s] IP: %s | AD_User: %s&lt;/span&gt;&lt;span&gt;\n&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;date&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;user_ip&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;username&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// FILE_APPEND évite d&apos;écraser le fichier à chaque soumission&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;file_put_contents&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;compteur.log&lt;/span&gt;&lt;span&gt;&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;logEntry&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;FILE_APPEND&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;LOCK_EX&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// Supprimer la redirection pour cette IP : l&apos;utilisateur retrouve Internet&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;sudo iptables -t nat -I PREROUTING -s &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;user_ip&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; -j ACCEPT&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;// Rediriger vers une page neutre après une seconde&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;    &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;&amp;#x3C;script&gt;setTimeout(function(){ window.location.href = &apos;https://www.google.com&apos;; }, 1000);&amp;#x3C;/script&gt;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;}&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;?&gt;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Si le journal &lt;code dir=&quot;auto&quot;&gt;compteur.log&lt;/code&gt; ne se crée pas, c’est une question de droits sur le répertoire, réglée par le &lt;code dir=&quot;auto&quot;&gt;chown -R www-data:www-data&lt;/code&gt; vu plus haut. Les permissions du script se fixent de la même façon :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;644&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/www/html/connect.php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;chown&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;www-data:www-data&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/www/html/connect.php&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;À ce stade, le montage est complet : un Wi-Fi ouvert, un portail crédible et un journal d’identifiants. Les deux sections suivantes poussent la démonstration plus loin pour rendre le risque tangible.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;aller-plus-loin-1--capture-passive-du-trafic&quot;&gt;Aller plus loin 1 : capture passive du trafic&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Pour montrer ce qui transite réellement sur un réseau ouvert, &lt;code dir=&quot;auto&quot;&gt;tcpdump&lt;/code&gt; enregistre le trafic web sur l’interface du hotspot. Un filtre limite la capture aux ports 80 et 443, ce qui écarte le SSH d’administration et les flux annexes :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;span&gt; &amp;#x26;&amp;#x26; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tcpdump&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;tcpdump&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wlan0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-v&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-w&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/var/www/html/capture.pcap&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;tcp port 80 or tcp port 443&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;-i wlan0&lt;/code&gt; écoute l’interface du hotspot.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;-v&lt;/code&gt; affiche le nombre de paquets capturés en temps réel.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;-w&lt;/code&gt; écrit la capture au format &lt;code dir=&quot;auto&quot;&gt;.pcap&lt;/code&gt;, ici dans le répertoire web pour la récupérer facilement.&lt;/li&gt;
&lt;li&gt;Le filtre &lt;code dir=&quot;auto&quot;&gt;&quot;tcp port 80 or tcp port 443&quot;&lt;/code&gt; cible le seul trafic web.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;La capture reste passive : elle montre le volume et les métadonnées, mais le contenu HTTPS demeure chiffré.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;aller-plus-loin-2--interception-active-avec-mitmproxy&quot;&gt;Aller plus loin 2 : interception active avec mitmproxy&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Pour lire le contenu des échanges, y compris HTTPS, on interpose un proxy transparent. &lt;code dir=&quot;auto&quot;&gt;mitmproxy&lt;/code&gt; remplit ce rôle. Il s’installe proprement via &lt;code dir=&quot;auto&quot;&gt;pipx&lt;/code&gt; :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;update&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;pipx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;python3-dev&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-y&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pipx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ensurepath&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;source&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;~/.bashrc&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;pipx&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mitmproxy&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;L’interface web &lt;code dir=&quot;auto&quot;&gt;mitmweb&lt;/code&gt; n’écoute par défaut que sur &lt;code dir=&quot;auto&quot;&gt;127.0.0.1&lt;/code&gt;. Pour la piloter depuis un autre poste du réseau, on l’ouvre sur toutes les interfaces :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;mitmweb&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--mode&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;transparent&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--web-host&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;0.0.0.0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--web-port&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;8081&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;--mode transparent&lt;/code&gt; intercepte le trafic dévié par &lt;code dir=&quot;auto&quot;&gt;iptables&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;--web-host 0.0.0.0&lt;/code&gt; rend l’interface accessible depuis un autre PC.&lt;/li&gt;
&lt;li&gt;&lt;code dir=&quot;auto&quot;&gt;--web-port 8081&lt;/code&gt; est le port de contrôle, à ne pas confondre avec le port 8080 qui reçoit le trafic intercepté.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;Interface web de mitmweb affichant les requêtes HTTP et HTTPS interceptées des clients du hotspot&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1680&quot; height=&quot;949&quot; src=&quot;https://blog.corentinbringer.fr/_astro/mitmproxy.CGmzr_VV_Z1XhV2e.webp&quot; srcset=&quot;&quot;&gt;&lt;/p&gt;
&lt;p&gt;Le trafic web des clients Wi-Fi doit maintenant partir vers le proxy, sans casser l’accès au portail local. C’est là que la table &lt;code dir=&quot;auto&quot;&gt;nat&lt;/code&gt; a demandé quelques allers-retours avant de tomber juste. La version qui tient la route repose sur un ordre précis et sur une exclusion du réseau local :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 1. Autoriser le trafic DNS, indispensable pour que le téléphone&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#    résolve les noms et déclenche la détection de portail&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iptables&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PREROUTING&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wlan0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;udp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;53&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-j&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ACCEPT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iptables&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PREROUTING&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wlan0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tcp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;53&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-j&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;ACCEPT&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 2. Rediriger le HTTP vers le portail captif local (10.42.0.1)&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#    pour déclencher la page PHP&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iptables&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PREROUTING&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wlan0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tcp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-j&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;DNAT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--to-destination&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10.42.0.1:80&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# 3. Intercepter tout le reste du trafic web sortant vers mitmproxy.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#    On exclut la destination 10.42.0.0/24 pour ne pas dévier&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;#    l&apos;accès au Nginx local, qui doit rester joignable.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iptables&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PREROUTING&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wlan0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tcp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10.42.0.0/24&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;80&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-j&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;REDIRECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--to-port&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;8080&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iptables&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-t&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;nat&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-A&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;PREROUTING&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-i&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;wlan0&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-p&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tcp&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;!&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-d&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;10.42.0.0/24&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--dport&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;443&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;-j&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;REDIRECT&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;--to-port&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;8080&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;L’exclusion &lt;code dir=&quot;auto&quot;&gt;! -d 10.42.0.0/24&lt;/code&gt; est la pièce qui débloque tout : le trafic à destination du Pi lui-même (le portail) passe par le &lt;code dir=&quot;auto&quot;&gt;DNAT&lt;/code&gt;, alors que le trafic vers Internet est capté par mitmproxy. La chaîne &lt;code dir=&quot;auto&quot;&gt;PREROUTING&lt;/code&gt; finale ressemble alors à ceci :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;span&gt;pkts bytes target     prot opt in     out   source        destination&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;  &lt;/span&gt;&lt;/span&gt;&lt;span&gt;106  7316 ACCEPT     udp  --  wlan0  *     0.0.0.0/0     0.0.0.0/0      udp dpt:53&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0     0 ACCEPT     tcp  --  wlan0  *     0.0.0.0/0     0.0.0.0/0      tcp dpt:53&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;29  1856 DNAT       tcp  --  wlan0  *     0.0.0.0/0     0.0.0.0/0      tcp dpt:80 to:10.42.0.1:80&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;    &lt;/span&gt;&lt;/span&gt;&lt;span&gt;0     0 REDIRECT   tcp  --  wlan0  *     0.0.0.0/0    !10.42.0.0/24   tcp dpt:80 redir ports 8080&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;&lt;span&gt;   &lt;/span&gt;&lt;/span&gt;&lt;span&gt;25  1600 REDIRECT   tcp  --  wlan0  *     0.0.0.0/0    !10.42.0.0/24   tcp dpt:443 redir ports 8080&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;div&gt;&lt;h2 id=&quot;rendre-le-montage-persistant&quot;&gt;Rendre le montage persistant&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Un redémarrage du Pi ou une réinitialisation de l’interface efface les règles &lt;code dir=&quot;auto&quot;&gt;iptables&lt;/code&gt;. Le paquet &lt;code dir=&quot;auto&quot;&gt;iptables-persistent&lt;/code&gt; les recharge au démarrage :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;apt&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iptables-persistent&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;# Sauvegarder l&apos;état courant après chaque modification&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;iptables-save&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;tee&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;/etc/iptables/rules.v4&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Le proxy doit lui aussi survivre à un redémarrage. Les premières règles &lt;code dir=&quot;auto&quot;&gt;iptables&lt;/code&gt; redirigent le trafic entrant vers le port 8080 ; si &lt;code dir=&quot;auto&quot;&gt;mitmweb&lt;/code&gt; est éteint, plus rien n’atteint le portail. Un service systemd le lance automatiquement.&lt;/p&gt;
&lt;p&gt;Dans &lt;code dir=&quot;auto&quot;&gt;/etc/systemd/system/mitmweb.service&lt;/code&gt; :&lt;/p&gt;
&lt;aside aria-label=&quot;Service lancé en root&quot;&gt;&lt;p aria-hidden=&quot;true&quot;&gt;Service lancé en root&lt;/p&gt;&lt;div&gt;&lt;p&gt;Le service tourne ici sous &lt;code dir=&quot;auto&quot;&gt;root&lt;/code&gt;, ce qui reste discutable pour un démon exposé. C’est un choix assumé de POC, pour aller vite sur une démo jetable. Sur un montage destiné à durer, il vaut mieux un utilisateur dédié avec les seules capacités réseau nécessaires.&lt;/p&gt;&lt;/div&gt;&lt;/aside&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[Unit]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Description&lt;/span&gt;&lt;span&gt;=Mitmweb Transparent Proxy Service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;After&lt;/span&gt;&lt;span&gt;=network.target&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[Service]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Type&lt;/span&gt;&lt;span&gt;=simple&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;User&lt;/span&gt;&lt;span&gt;=root&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;WorkingDirectory&lt;/span&gt;&lt;span&gt;=/root&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;ExecStart&lt;/span&gt;&lt;span&gt;=/root/.local/bin/mitmweb --mode transparent --web-host 0.0.0.0 --web-port 8081 --set &lt;/span&gt;&lt;span&gt;web_password&lt;/span&gt;&lt;span&gt;=MotDePasseInterface&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;Restart&lt;/span&gt;&lt;span&gt;=always&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;RestartSec&lt;/span&gt;&lt;span&gt;=5&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;
&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;[Install]&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;WantedBy&lt;/span&gt;&lt;span&gt;=multi-user.target&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;L’option &lt;code dir=&quot;auto&quot;&gt;--set web_password&lt;/code&gt; protège l’interface de contrôle par un mot de passe. On active ensuite le service :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;daemon-reload&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;enable&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mitmweb.service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;start&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mitmweb.service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;status&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;mitmweb.service&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Le service doit apparaître &lt;code dir=&quot;auto&quot;&gt;active (running)&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;configurer-lurl-du-tableau-de-bord&quot;&gt;Configurer l’URL du tableau de bord&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Le portail appelle l’API d’un tableau de bord externe pour incrémenter deux compteurs : les personnes qui ont simplement ouvert la page du portail, et celles qui sont allées jusqu’à saisir leurs identifiants. Cela donne une mesure de l’exposition et du taux de clic réel, matière directe pour la restitution de sensibilisation. Pour garder le même code d’un environnement à l’autre, on externalise l’URL de cette API dans une variable d’environnement plutôt que de la coder en dur.&lt;/p&gt;
&lt;p&gt;Le point à retenir : c’est PHP-FPM qui injecte la variable, pas Nginx. La déclaration se fait dans le pool PHP, &lt;code dir=&quot;auto&quot;&gt;/etc/php/8.4/fpm/pool.d/www.conf&lt;/code&gt; :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;env[DASHBOARD_URL] = http://192.168.1.50:8080&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Un redémarrage du service prend la variable en compte :&lt;/p&gt;
&lt;div&gt;&lt;figure&gt;&lt;figcaption&gt;&lt;span&gt;&lt;/span&gt;&lt;/figcaption&gt;&lt;pre&gt;&lt;code&gt;&lt;div&gt;&lt;div&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;systemctl&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;restart&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;span&gt;php8.4-fpm&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;&lt;/div&gt;
&lt;p&gt;Le code PHP la lit ensuite via &lt;code dir=&quot;auto&quot;&gt;getenv(&apos;DASHBOARD_URL&apos;)&lt;/code&gt;.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Le résultat tient dans un boîtier discret, glissé dans un faux plafond, qui diffuse un Wi-Fi au nom de l’entreprise et collecte les identifiants de ceux qui s’y connectent. La chaîne complète, du portail captif au proxy transparent, tient sur un Raspberry Pi à quelques dizaines d’euros.&lt;/p&gt;
&lt;p&gt;L’intérêt de l’exercice n’est pas la prouesse technique mais la restitution. Confronter un collaborateur à son propre identifiant, saisi sur un faux portail quelques minutes plus tôt, marque durablement plus qu’un rappel de consigne. C’est le principe de la purple team : l’attaque ne vaut que par la défense qu’elle permet de construire derrière, ici une vigilance accrue face aux réseaux ouverts et une politique claire sur la saisie d’identifiants Active Directory hors des applications maîtrisées.&lt;/p&gt;
&lt;p&gt;Reste une part de plaisir assumée, celle de bricoler un petit boîtier discret et de le glisser dans un faux plafond. C’est un projet de sensibilisation avant tout, mais qui m’a bien amusé à monter.&lt;/p&gt;
</content:encoded><category>raspberry-pi</category><category>wifi</category><category>portail-captif</category><category>purple-team</category><category>iptables</category><category>mitmproxy</category></item><item><title>HTTP QUERY : un verbe pour les lectures avec un corps</title><link>https://blog.corentinbringer.fr/blog/http-query-rfc10008/</link><guid isPermaLink="true">https://blog.corentinbringer.fr/blog/http-query-rfc10008/</guid><description>POST a longtemps servi de palliatif pour les requêtes de lecture trop grandes pour une URL. La RFC 10008 officialise enfin un verbe dédié à ce cas.</description><pubDate>Tue, 30 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;Mise à jour :&lt;/strong&gt; depuis la publication initiale de ce billet, le draft est devenu la RFC 10008, publiée en tant que Proposed Standard. L’implémentation dans les frameworks n’est pas encore là, mais la réponse officielle existe.&lt;/p&gt;
&lt;hr&gt;
&lt;div&gt;&lt;h2 id=&quot;le-problème&quot;&gt;Le problème&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Quand un filtre de recherche dépasse ce qu’une URL peut raisonnablement transporter, la solution de fait est toujours la même : mettre le corps dans un POST et passer à autre chose.&lt;/p&gt;
&lt;p&gt;Ça fonctionne. Mais ça casse des propriétés que HTTP offre gratuitement sur les vraies lectures.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Le cache ne s’active pas.&lt;/strong&gt; POST signifie «  écriture  » pour les proxies et les CDN. La réponse ne sera pas mise en cache, même si elle est identique d’une requête à l’autre.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Les retries deviennent risqués.&lt;/strong&gt; POST n’est pas idempotent par définition. Un client qui reçoit une erreur réseau ne sait pas s’il peut réémettre la requête sans effet de bord.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La sémantique disparaît.&lt;/strong&gt; Un proxy, un load balancer ou un log d’audit voit un POST et présume une modification d’état. Il n’a aucun moyen de distinguer ça d’une simple lecture filtrée.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Ce n’est pas un bug de conception de votre API. C’est une limite du protocole, et les développeurs la contournent depuis des années avec les mêmes workarounds.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;ce-que-query-apporte&quot;&gt;Ce que QUERY apporte&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;La RFC 10008 définit la méthode &lt;code dir=&quot;auto&quot;&gt;QUERY&lt;/code&gt;. Elle combine ce qui manquait : un corps de requête, avec la sémantique d’une lecture.&lt;/p&gt;
&lt;p&gt;Concrètement :&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Corps libre.&lt;/strong&gt; Comme POST, QUERY transporte un corps. Vous pouvez y mettre un objet JSON arbitrairement complexe, un filtre GraphQL, une requête SQL partielle, peu importe le format que votre ressource comprend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Sûre et idempotente.&lt;/strong&gt; Comme GET, QUERY déclare qu’elle ne modifie pas l’état du serveur. Les clients peuvent la réémettre sans risque après une coupure réseau. Les proxies peuvent l’identifier comme une lecture.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Compatible avec le cache.&lt;/strong&gt; Les intermédiaires peuvent mettre la réponse en cache et la servir à d’autres clients qui envoient la même requête. C’est impossible avec POST sans configuration explicite et fragile.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Le protocole ajoute aussi un en-tête &lt;code dir=&quot;auto&quot;&gt;Accept-Query&lt;/code&gt;. Une ressource peut l’utiliser pour annoncer les formats de corps qu’elle accepte, de la même façon qu’&lt;code dir=&quot;auto&quot;&gt;Accept&lt;/code&gt; annonce les formats de réponse.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;où-on-en-est&quot;&gt;Où on en est&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;La RFC 10008 est publiée en tant que &lt;strong&gt;Proposed Standard&lt;/strong&gt; à l’IETF. C’est une étape formelle : le problème est reconnu, la solution est documentée et validée.&lt;/p&gt;
&lt;p&gt;En pratique, aucun framework web majeur ne l’implémente encore nativement. Les navigateurs ne l’envoient pas. Les CDN ne savent pas encore quoi en faire. La route entre un Proposed Standard et une adoption réelle dans une stack de production prend du temps, et QUERY ne fait pas exception.&lt;/p&gt;
&lt;p&gt;Ce que ça change dès maintenant : si vous contrôlez à la fois le client et le serveur d’une API interne, rien ne vous empêche de l’implémenter vous-même. La méthode est déclarée, la sémantique est claire. Un serveur qui comprend &lt;code dir=&quot;auto&quot;&gt;QUERY&lt;/code&gt; sur une route &lt;code dir=&quot;auto&quot;&gt;/search&lt;/code&gt; reste parfaitement conforme à HTTP.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;ce-que-ça-règle-vraiment&quot;&gt;Ce que ça règle vraiment&lt;/h2&gt;&lt;/div&gt;
&lt;p&gt;Le problème n’est pas technique. Les développeurs ont des solutions qui fonctionnent depuis des années : POST avec un corps JSON, GET avec un paramètre encodé en base64, parfois GraphQL pour s’abstraire du problème. Ça tourne.&lt;/p&gt;
&lt;p&gt;Ce que QUERY règle, c’est la sémantique. HTTP repose sur des conventions que les intermédiaires comprennent sans configuration : méthode sûre = lisible par le cache, idempotente = rejouable par le client. En utilisant POST pour des lectures, on sort de ces conventions et on perd les bénéfices associés, souvent sans s’en rendre compte.&lt;/p&gt;
&lt;p&gt;Avoir un verbe explicite pour ce cas permet de retrouver ces propriétés sans workaround. C’est un ajustement de protocole, pas une refonte, et c’est exactement ce dont ce cas avait besoin.&lt;/p&gt;
&lt;div&gt;&lt;h2 id=&quot;sources&quot;&gt;Sources&lt;/h2&gt;&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://datatracker.ietf.org/doc/rfc10008/&quot;&gt;RFC 10008 – The HTTP QUERY Method&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</content:encoded><category>http</category><category>rfc</category><category>api</category><category>rest</category></item></channel></rss>