21h14, un utilisateur est exclu. 21h15, quelqu'un se connecte. Autre pseudo, autre adresse IP, autre fournisseur d'accès. Il salue le salon exactement comme l'autre le faisait, avec la même faute au même endroit.
Tout le monde sait que c'est lui. Reste à savoir comment on le sait, parce qu'entre « tout le monde sait » et « je peux le montrer », il y a le sujet de cet article.
J'ai raconté ailleurs comment on apprend ça, à l'époque des clients IRC classiques. Ce que je gère aujourd'hui est un réseau IRC complet : plusieurs serveurs, des dizaines de salons, et jusqu'à plus de mille personnes connectées en même temps. Avec une différence qui change tout — l'accès en est bridé, on ne s'y connecte qu'avec un navigateur. Pas de client tiers, pas de connexion directe.
Ce billet-ci est donc la version technique et à jour : ce que le serveur voit vraiment, indice par indice, avec ce que chacun vaut et ce qu'il ne vaut pas.
Ce que le serveur voit vraiment
Pas de magie. Un serveur de chat ne voit que trois familles de choses :
- ce que la connexion apporte : une adresse IP, l'heure, la durée, parfois le chemin réseau ;
- ce que le client déclare : un pseudo, un âge, un sexe, une langue, un identifiant de session, tout ce que le navigateur envoie dans ses en-têtes ;
- ce que la personne fait : l'heure à laquelle elle vient, la façon dont elle écrit, à qui elle parle.
La première famille est difficile à falsifier mais peu précise. La deuxième est précise mais entièrement déclarative, donc falsifiable en deux clics. La troisième est la plus difficile à maquiller, et c'est la moins technique. Cette asymétrie est tout le problème.
Une remarque sur la deuxième, qui découle directement du choix de brider l'accès au web : tout le monde entre par le même chemin, donc ces signaux existent pour chaque utilisateur, sans exception. Sur un réseau ouvert aux clients IRC classiques, cette famille est presque vide — un client ne raconte à peu près rien de la machine qui l'exécute. Le navigateur, lui, est bavard par conception.
L'adresse IP, et ce qu'elle ne dit pas
C'est le premier réflexe, et c'est le plus mal compris.
Une IP ne désigne pas une personne. Elle désigne un point de raccordement, à un instant donné. Derrière une seule adresse, il peut y avoir une famille entière, une classe, un open space, un hall de gare. Chez un opérateur mobile, le partage d'adresses fait que des milliers d'abonnés sortent par la même IP au même moment. Deux comptes vus sur la même adresse, ce n'est donc pas « la même personne » : c'est « la même sortie réseau », ce qui n'est pas du tout la même affirmation.
Et depuis que tout le monde discute depuis un téléphone, c'est devenu une vraie galère. Une IP mobile ne tient pas en place : elle change en changeant de cellule, en passant d'un réseau à l'autre, en sortant d'un tunnel, au retour du mode avion, ou simplement parce que l'opérateur fait tourner ses pools. La même personne peut en changer trois fois dans une soirée sans avoir rien fait de particulier, et sans même s'en apercevoir. Résultat : trois adresses pour un seul utilisateur, et la même adresse pour deux utilisateurs à vingt minutes d'écart. Un bannissement par IP, dans ces conditions, rate sa cible et touche quelqu'un d'autre.
Ce qui est exploitable, en revanche :
- le réseau auquel elle appartient, pas sa valeur exacte. Un changement d'adresse chez le même fournisseur, dans la même plage, après une simple reconnexion de box, est un signal faible ; passer de la plage résidentielle d'un opérateur à celle d'un hébergeur, c'est autre chose : ça veut dire VPN, serveur ou relais ;
- la nature de l'hébergement. Un utilisateur qui arrive d'un centre de données ne vient pas de son canapé. Ça ne prouve rien — beaucoup de gens utilisent un VPN par habitude, et c'est leur droit — mais l'enchaînement « exclusion, puis retour immédiat depuis un VPN » vaut mieux que les deux faits pris séparément ;
- la localisation, avec ses limites réelles. Une base de géolocalisation donne au mieux une ville, souvent celle du point de collecte de l'opérateur, parfois le centre géographique du pays quand elle ne sait pas. Traiter ça comme une adresse postale est la première erreur du débutant ;
- l'IPv6, qu'on oublie et qui est plus bavard. Le préfixe attribué à un logement reste souvent stable pendant des mois, là où l'IPv4 tourne. Deux comptes sous le même préfixe, c'est plus fort que deux comptes sous la même IPv4 partagée.
Ce que le navigateur raconte sans qu'on lui demande
Un client web arrive avec une petite valise. Ce qu'il y a dedans est déclaratif, donc modifiable, mais peu de gens le modifient, et c'est justement ce qui le rend utile.
Le cookie de session est le plus simple. Il identifie le navigateur, pas la personne. Il disparaît au premier nettoyage, et il n'existe pas en navigation privée. C'est un indice fort quand il est là, et son absence ne veut rien dire.
Le stockage local survit à davantage de choses qu'on ne le croit — beaucoup de gens « effacent les cookies » sans toucher au reste. Même remarque : fort quand il est là, muet sinon.
L'en-tête de langues est sous-estimé. Ce n'est pas « français » : c'est une liste ordonnée, avec des variantes régionales et des pondérations. La chaîne exacte d'un navigateur configuré en français de Belgique, puis anglais, puis néerlandais, n'est pas si courante. Prise seule elle ne vaut rien ; rapprochée d'autre chose, elle discrimine bien.
L'agent utilisateur donne le navigateur, sa version, le système. Sa force n'est pas dans sa valeur mais dans sa trajectoire : un navigateur se met à jour à intervalles réguliers, et deux comptes qui changent de version le même jour dans la même heure ont de bonnes chances de tourner sur la même machine.
Le référent dit d'où la personne arrive : un moteur de recherche, un lien posté sur un forum, un favori. Deux comptes créés à dix minutes d'intervalle depuis le même lien d'un site tiers, c'est une coïncidence qui commence à coûter cher.
Le fuseau horaire et la taille de l'écran sont livrés par la page elle-même. Anodins isolément, discriminants combinés.
Au-delà, il y a tout l'arsenal de l'empreinte de navigateur : polices installées, rendu graphique, signature de la couche de chiffrement. C'est efficace, et je m'en tiens volontairement à distance sur un chat grand public. Non pas parce que ça ne marche pas, mais parce que ça marche trop bien pour l'usage : je tiens un réseau de discussion, je ne piste pas des gens à travers le web. La frontière est là, et je préfère l'avoir tracée avant d'en avoir besoin.
Le pseudo, l'âge, le sexe : ce qui est déclaré
Tout ce que l'utilisateur écrit sur lui-même est une donnée sur ce qu'il veut montrer, pas sur ce qu'il est. Ça reste utile, à deux conditions.
D'abord, on ne regarde pas la valeur mais la constante. Quelqu'un qui revient reprend presque toujours le même âge, le même sexe, la même région — non par imprudence, mais parce qu'inventer un personnage cohérent et s'y tenir demande un effort que personne ne fournit pour aller discuter un mardi soir.
Avec une nuance qui compte, et qui sépare en fait deux populations. Ce qui précède vaut pour l'immense majorité des exclus : des gens agaçants, pas des professionnels de la dissimulation. Celui qui a eu un comportement réellement perturbateur, ou qui est allé jusqu'à l'illégal, ne se contente pas de changer de pseudo — il revient sous une identité entièrement refaite, âge, sexe, région, histoire personnelle comprises. Il sait pourquoi il a été exclu et il sait ce qu'on va chercher.
Et c'est une information en soi : à partir du moment où le personnage change complètement, on n'a plus affaire au même type d'utilisateur. Les indices déclaratifs s'effondrent d'un coup, et il ne reste que les deux bouts de la chaîne — ce que la connexion apporte malgré lui, et ce qu'il ne sait pas s'empêcher de faire. C'est plus long, c'est moins confortable, et c'est là que le reste de cet article sert vraiment à quelque chose.
Ensuite, on regarde la forme du pseudo autant que son contenu. Les gens ont une grammaire : chiffres en fin, séparateur préféré, majuscule ou pas, même racine déclinée. TitiDu59 devient Titi_59, puis titi59bis. Ce n'est pas de la déduction, c'est de l'observation.
L'ident, du vestige au marqueur
Sur IRC, l'ident est ce petit identifiant qui précède le nom d'hôte. À l'origine il était vérifié par un service tournant sur la machine du client ; en pratique, sur un réseau ouvert aux clients classiques, c'est le client qui le déclare et personne ne le contrôle.
Il n'a donc jamais valu grand-chose comme preuve d'identité, et beaucoup comme empreinte de configuration : la plupart des gens n'y touchaient jamais, et il restait celui que le logiciel avait mis par défaut, parfois le nom de session de la machine. Un ident inhabituel qui réapparaissait sous trois pseudos différents, c'était le même logiciel sur le même poste. Le même raisonnement que l'agent utilisateur aujourd'hui, à l'époque où les machines se présentaient poliment.
Sur un réseau où l'on n'entre que par le web, la situation s'inverse complètement. Ce n'est plus l'utilisateur qui déclare son ident, c'est ma passerelle — et ce qu'on écrit dedans, c'est moi qui le décide.
C'est donc ce que je fais. Le client web, que je maîtrise puisque c'est moi qui le sers, calcule un identifiant, le range dans le navigateur, et le présente comme ident à chaque connexion. Une vingtaine de lignes dans un greffon :
kiwi.plugin('username', function (kiwi) {
const UA = navigator.userAgent;
// Ordre impératif : Edge, Opera et Yandex annoncent tous « Chrome »
// dans leur user-agent. Ils passent donc AVANT lui.
function navigateur() {
if (/Edg\/|Edge\//.test(UA)) return 'e';
if (/OPR\/|Opera/.test(UA)) return 'p';
if (/Firefox|FxiOS/.test(UA)) return 'f';
if (/Chrome|CriOS/.test(UA)) return 'c';
if (/Safari/.test(UA)) return 's';
return 'o';
}
// Ordre impératif aussi : un user-agent Android contient « Linux »,
// et un iPad récent se présente comme un Mac.
function systeme() {
if (/iPad|iPhone|iPod/.test(UA)) return 'i';
if (/Mac/.test(UA) && 'ontouchend' in document) return 'i';
if (/Android/i.test(UA)) return 'a';
if (/Windows/.test(UA)) return 'w';
if (/Mac/.test(UA)) return 'm';
if (/Linux|X11/.test(UA)) return 'l';
return 'o';
}
// La propriété est effectiveType, en camelCase.
function reseau() {
const c = navigator.connection;
if (!c || !c.effectiveType) return 'u';
return /g$/.test(c.effectiveType) ? 'm' : 'o'; // 2g, 3g, 4g…
}
// Six caractères lisibles, puis trois au hasard.
function marqueur() {
return source() + navigateur() + systeme()
+ appareil() + reseau() + langue() + aleatoire(3);
}
function identifiant() {
// on lit dans l'un OU l'autre, on réécrit dans LES DEUX
let id = lireStockage('username') || lireCookie('username');
if (!id) id = marqueur();
ecrireStockage('username', id);
ecrireCookie('username', id, 30);
return id;
}
kiwi.state.$on('network.new', function (event) {
event.network.username = identifiant();
});
});
Le détail qui fait tout le travail est la redondance : on lit l'identifiant dans le stockage local ou dans le cookie, et on le réécrit dans les deux. Effacer ses cookies seuls, ou vider le stockage local seul, ne suffit donc pas à repartir de zéro — et ce sont les deux gestes que les gens font, jamais ensemble.
Les deux commentaires « ordre impératif » ne sont pas décoratifs. C'est exactement là que ce genre de code se trompe en silence : tester Chrome avant Edge range tous les Edge parmi les Chrome, tester Linux avant Android range tous les téléphones parmi les machines de bureau, et écrire effectivetype au lieu de effectiveType renvoie « inconnu » pour tout le monde. Rien ne plante, rien ne s'affiche dans la console, et le marqueur continue de fonctionner puisque l'aléatoire le rend unique.
Ce qui est perdu, ce sont les axes. On croit lire six informations dans un préfixe qui n'en porte plus que trois, et on fonde des rapprochements sur des caractères qui valent la même chose pour tout le monde. Un indicateur qui répond toujours la même chose est pire qu'un indicateur absent : il ne manque pas, il rassure.
Le champ le plus inutile du protocole devient ainsi le plus fiable, parce qu'il cesse d'être quelque chose que l'utilisateur affirme pour devenir quelque chose que j'ai écrit moi-même. Un revenant arrive avec un nouveau pseudo et une nouvelle adresse, et le même marqueur : la modération le voit directement dans le masque, sans avoir à croiser quoi que ce soit.
Deux remarques tout de suite, parce qu'un marqueur commode se prend vite pour une preuve.
Il identifie un navigateur, pas une personne. Une fenêtre privée, un autre appareil, un nettoyage complet, et il repart à zéro ; à l'inverse, deux personnes sur le même ordinateur familial le partagent. C'est exactement l'indice qui m'a joué le tour raconté plus bas.
Et il est visible par tout le salon, puisqu'il s'affiche dans le masque. Les six premiers caractères sont lisibles à dessein, pour que la modération voie d'un coup d'œil d'où vient quelqu'un et avec quoi. Cela veut dire que n'importe qui dans le salon peut les lire aussi. C'est un compromis que j'assume, parce que ces six caractères ne décrivent que ce que le navigateur raconte déjà à tous les serveurs qu'il visite, et qu'ils ne désignent personne. Le jour où j'y mettrais une information propre à la personne, ce ne serait plus un marqueur, ce serait une fuite.
Avec une conséquence qu'il faut avoir en tête, parce qu'elle conditionne tout le reste : c'est la passerelle qui décide de ce que le serveur voit. Mal réglée, elle présente l'ensemble des utilisateurs comme venant d'elle-même, et on perd d'un coup l'adresse réelle de tout le monde. Elle doit donc transmettre explicitement l'IP d'origine au serveur. Ça se vérifie le jour de l'installation, et ça se revérifie après chaque mise à jour : une passerelle revenue à ses valeurs par défaut aplatit tous les indices de cet article en même temps.
Ce qu'on ne change pas facilement
Voilà la partie que les gens sous-estiment, et de loin. On change d'adresse IP en redémarrant une box. On ne change pas sa façon d'écrire.
- La langue. Les tics, les formules d'entrée, la ponctuation, l'espace avant le point d'interrogation ou son absence, les abréviations, la même faute au même mot. C'est une signature, et elle résiste au changement de pseudo parce que personne ne peut se relire en permanence.
- Le rythme. Les horaires de connexion, la durée des sessions, les jours. Quelqu'un qui vient tous les soirs entre 21h et minuit revient entre 21h et minuit, y compris après un bannissement.
- Le retour lui-même. Le délai entre l'exclusion et la reconnexion est un indice en soi. Quarante secondes, c'est quelqu'un qui était devant son écran, contrarié.
- Le graphe social. C'est le plus solide de tous. Un revenant retrouve les mêmes interlocuteurs, les mêmes salons, et se fait souvent reconnaître par ses habitués avant que la modération n'ait rien remarqué. On peut se fabriquer une nouvelle identité ; on se refait très difficilement un nouveau cercle.
Ces quatre-là ne se lisent dans aucun en-tête. Ils demandent de l'attention et des journaux tenus proprement, et ils valent mieux que n'importe quelle astuce technique.
La constellation d'indices
Aucun de ces indices ne désigne quelqu'un. C'est la constellation qu'ils forment qui parle, et il faut faire le calcul honnêtement.
L'exemple que tu croises tout le temps : deux comptes, deux fournisseurs d'accès différents, mais la même ville. Pris seul, ça ne vaut rien — une ville, c'est des dizaines de milliers d'abonnés chez chaque opérateur. Maintenant, ajoutons : même liste de langues exacte, même version de navigateur, connexions dans la même tranche horaire, mêmes trois interlocuteurs, et une reconnexion quarante secondes après une exclusion. Chacun de ces éléments est faible. Ensemble, ils ne le sont plus du tout, parce que la probabilité de les réunir par hasard chez deux personnes distinctes s'effondre à chaque terme ajouté.
Le raisonnement inverse est tout aussi important : quel est le nombre de gens que cet indice désigne ? Une IP de bureau ou d'école en désigne des centaines. Un préfixe IPv6 résidentiel en désigne trois ou quatre. Un stockage local identique en désigne un, sauf poste partagé. Écrire ce chiffre, même approximatif, à côté de chaque indice, change complètement la conclusion qu'on en tire.
Deux indices qui disent la même chose ne comptent pas double. Même ville et même fournisseur d'accès, ce n'est pas deux indices : c'est le même, vu deux fois. En revanche, une constellation faite de choses de natures différentes — le réseau, le navigateur, l'écriture, l'horaire — vaut bien plus que trois indices de la même famille.
Mon erreur, et ce qui n'en est pas une
Il faut d'abord dire ce qui n'en est pas une, parce que ça surprend ceux qui abordent le sujet par le côté enquête : il m'arrive de bannir tout un foyer, toute une plage d'adresses, parfois de fait une petite ville entière, en sachant très bien que je touche des gens qui n'ont rien fait.
Ce n'est pas une bavure, c'est un arbitrage. Je tiens un tchat, pas un tribunal. Le coût d'un faux positif ici, c'est quelqu'un qui ne discute pas ce soir-là et qui m'écrit pour le dire — c'est réversible en trente secondes. Le coût d'un perturbateur qui revient dix fois, c'est un salon que les habitués finissent par déserter. Entre les deux je choisis, et je peux me le permettre : à l'échelle d'Internet, un réseau comme le mien reste petit, même avec mille personnes connectées. La question n'est alors pas « est-ce lui », mais « est-ce que ça vaut le coup ». Ce n'est pas la même question, et elle ne demande pas le même niveau de certitude.
L'erreur est ailleurs, et celle-là je l'ai bien faite.
Deux comptes, une même adresse IP, des horaires qui se chevauchent, des sujets proches. J'en ai conclu au multicompte, et surtout je l'ai dit : j'ai sanctionné en annonçant que c'était la même personne. C'étaient deux personnes du même foyer, et celle qui n'avait rien fait l'a très mal pris. À raison.
Ce qui m'a trompé n'est pas l'indice, il était juste : cette IP était bien commune aux deux. C'est ce que j'en ai déduit, et la façon dont je l'ai formulé. J'avais devant moi un seul indice, vu deux fois, plus des éléments qui découlaient tous du fait de vivre sous le même toit. Aucune constellation : rien que la même étoile, regardée sous deux angles.
La leçon n'est donc pas « ne jamais toucher quelqu'un qui n'a rien fait » — ça m'arrive, et c'est assumé. Elle est plus fine que ça : bloquer est une décision, affirmer que deux comptes sont la même personne est une conclusion. La première se prend sur un rapport entre le coût et le bénéfice. La seconde demande une constellation. Depuis, je bannis toujours large quand il le faut, mais je n'accuse plus sans au moins un élément qui ne vienne ni du réseau ni du foyer.
C'est la traduction pratique d'une phrase que je répète depuis mes vingt ans de modération : un indice n'est pas une preuve.
Ce que je m'interdis
Un mot là-dessus, parce que ça fait partie du métier autant que le reste.
Une adresse IP est une donnée personnelle. Des journaux de connexion en sont aussi. Les garder demande une raison, une durée, et une information claire aux utilisateurs : les règles du réseau disent ce qui est conservé et combien de temps, et ce n'est pas une formalité juridique, c'est ce qui rend la modération défendable quand quelqu'un conteste.
Trois lignes de conduite, donc, et la première n'est pas celle qu'on met d'habitude dans ce genre de paragraphe.
On journalise, et autant l'assumer. Les salons publics sont enregistrés. Ça sert à modérer, à traiter une plainte qui arrive trois jours après les faits, à sortir des statistiques de fréquentation, à comprendre pourquoi un serveur a décroché un soir. Un exploitant qui jure ne rien garder ment, ou ne sait pas ce que fait sa machine. La vraie question n'est donc pas « est-ce que j'enregistre », mais quoi, pourquoi, pour combien de temps, et qui peut le relire — et sur ce dernier point, l'accès aux journaux n'est pas ouvert à toute l'équipe.
Une durée tenue, et le détail qu'on jette. C'est là que se fait le tri : une statistique de fréquentation a besoin de compteurs, pas du texte des conversations. Donc on garde le détail le temps utile à la modération, on agrège, et on se débarrasse du reste. Des journaux bruts conservés indéfiniment ne servent plus à tenir un salon, ils servent à autre chose.
La finalité. Je cherche à savoir si une personne exclue est revenue sur le réseau, pas qui elle est dans la vie. Ce sont deux questions différentes, et seule la première est la mienne.
La seconde relève d'une autorité, avec une réquisition. La confusion des deux est la façon la plus sûre de transformer un travail légitime en problème.
Ce que je retiens
- Une adresse IP désigne une sortie réseau, pas une personne. La question utile n'est jamais « quelle est son IP » mais « combien de gens cette IP désigne-t-elle ».
- Ce que le client déclare est précis et faux à volonté ; ce que la personne fait est flou et presque incorrigible. Les indices qui tiennent le mieux dans la durée sont les moins techniques.
- Une constellation ne vaut que si elle traverse des familles différentes. Réseau, navigateur, écriture, horaire. Trois indices de la même famille, c'est un seul indice.
- Un indice n'est pas une preuve, et un faux positif se paie auprès de quelqu'un qui n'avait rien fait. C'est ce qui coûte le plus cher, et c'est ce qu'on oublie le plus vite.
Le reste est une affaire de journaux tenus correctement et d'attention. Les outils changent, et se recyclent parfois — un champ oublié des années 90 qui redevient le plus utile de tous — la méthode ne bouge pas d'un pouce.