Une alerte par robot, c'est zéro alerte
Il y a deux jours, j'écrivais ici que Sentinelle, mon bundle de sécurité Symfony, n'envoyait plus d'e-mail pour une adresse déjà bloquée dont toutes les tentatives avaient échoué. C'était le réglage quiet_when_blocked, activé par défaut depuis la version 0.4.
C'était faux. Ce réglage ne s'est jamais appliqué.
Le bug
Quand un robot dépasse le seuil (trop de requêtes vers des fichiers sensibles en quelques minutes), Sentinelle fait deux choses : il bloque l'adresse, puis il prépare l'alerte. Le service d'alerte sait se taire si on lui dit « cette adresse vient d'être bloquée, et elle n'a rien obtenu ». Il était testé, et ses tests passaient.
Sauf que le code qui l'appelait ne lui transmettait jamais le blocage qu'il venait de poser. Pour le service d'alerte, l'adresse n'était donc jamais bloquée. Résultat : il envoyait l'e-mail à chaque fois, avec en prime une phrase fausse, « non bloquée automatiquement », alors qu'elle l'était.
Chaque pièce marchait seule. C'est le fil entre les deux qui manquait. La version 0.6 corrige ce fil, et ajoute le test qui manquait : celui qui vérifie que l'appelant passe bien le blocage au service d'alerte, pas seulement que le service sait quoi en faire.
Le vrai problème : trop d'e-mails
Ce bug n'aurait pas tout réglé de toute façon. Un site public est scanné toute la journée. Sur un de mes sites en production, ces e-mails montaient à environ 200 par semaine. Presque tous racontaient la même histoire : une adresse cherche /.env, reçoit une 404, se fait bloquer. Il ne s'est rien passé.
À ce rythme, on apprend à ignorer les e-mails, puis à les filtrer, puis à les archiver sans les lire. Et le jour où l'un d'eux dit « un fichier sensible a vraiment été servi », il ressemble aux deux cents autres. C'est comme ça qu'on rate un vrai incident : pas faute d'alerte, mais à cause de trop d'alertes.
Sentinelle 0.6 : un e-mail seulement quand quelque chose est passé
Depuis la 0.6, une seule sorte d'alerte part immédiatement par défaut : une fuite. Une adresse sensible (.env, .git, clés, sauvegardes de base) qui a répondu avec succès, ou un secret repéré dans une réponse. Pas « quelqu'un a essayé » : « la réponse est sortie ». Cet e-mail-là est rouge, en priorité haute, et dit quoi faire maintenant.
Le reste n'est pas ignoré. Les scans, les tentatives d'injection, la force brute sont toujours détectés, bloqués et notés. Ils sont simplement comptés dans un résumé quotidien :
- les alertes du jour par type, et si ce type part à la minute ou non ;
- les réponses sensibles servies, en rouge, en premier ;
- les requêtes hostiles par type ;
- les adresses bloquées par motif, sans le détail, pour que cent scans tiennent sur une ligne ;
- les dix adresses les plus actives, et si elles sont encore bloquées.
Un jour sans rien à dire, aucun e-mail ne part.
Chacun choisit ce qui mérite un e-mail immédiat :
sentinelle:
alert:
immediate: [breach] # par défaut depuis la 0.6
# immediate: [breach, exploitation] # aussi chaque tentative d'exploitation
# immediate: all # l'ancien comportement
Pour retrouver exactement les e-mails d'avant : immediate: all et quiet_when_blocked: false.
Ce que j'en retiens
Une alerte doit répondre à une seule question : est-ce que je dois agir maintenant ? Une adresse qui s'est vu tout refuser ne demande rien. Une adresse qui a reçu votre .env demande une action dans l'heure. Les deux ne doivent pas arriver dans la même boîte, sous la même forme.
Et une leçon plus large, que je garde pour la suite : un réglage documenté et testé n'est pas un réglage qui marche. Le test doit suivre le chemin réel, de la requête à l'e-mail, pas seulement la dernière pièce.
Je préfère écrire cet article que laisser l'ancien dire quelque chose de faux. Si vous utilisez Sentinelle, la 0.6 se met à jour avec composer update acencyril/sentinelle-bundle ; pensez à planifier sentinelle:digest une fois par jour, sinon les alertes retenues ne sont visibles que dans le tableau de bord.
À lire aussi
L'IA écrit mon code, je garde les clés
Je ne tape plus de code : une IA écrit tout. Elle n'a pourtant accès ni au serveur ni à un seul secret. Qui peut faire quoi, ce qui est en place, et ce qui ne l'est pas encore.
Mon site se met à jour pendant que je dors, et je valide le matin
Uplift fait monter PHP, Symfony et les dépendances dans des limites que je fixe, lance les tests et me laisse décider. Premier vrai passage : Symfony 7.3 vers 7.4.21, 40 paquets.
Faire passer un crash-test à un agent IA
Un agent IA branché sur de vrais outils doit tenir quand on le pousse. CrashMire le met sous pression avec des données fictives, et dit ce qui a cédé et quels garde-fous ajouter.