Ce blog n'a d'autre prétention que de me permettre de mettre à la disposition de tous des petits textes que j'écris. On y parle surtout d'informatique mais d'autres sujets apparaissent parfois.
Première rédaction de cet article le 19 mars 2024
Le 16 février a été publiée la faille de sécurité DNSSEC KeyTrap. Je sais, c'est un peu tard pour en parler mais c'est quand même utile, non ?
KeyTrap est une faille qui permet un déni de service contre des résolveurs DNS validants, c'est-à-dire qui vérifient les signatures cryptograpĥiques de DNSSEC. Avec peu de messages DNS, parfois un seul, elle permet de stopper toute résolution de noms pendant des minutes, voire des heures. Comme souvent, hélas, en sécurité informatique, les découvreurs de la faille ont sérieusement abusé des grands mots dans leur communication (que les médias, comme d'habitude, ont bien relayé sans esprit critique) mais la faille est réelle et ils ont fait un bon travail pour la cerner exactement, et la reproduire afin de tester des remèdes.
Comment fonctionne KeyTrap ? Le point de départ est d'observer qu'un résolveur validant ne cherche pas à échouer dans la résolution de noms, il cherche à trouver, si nécessaire en insistant, un enregistrement correctement signé. Par exemple, si un des serveurs faisant autorité ne renvoie pas de signatures (il devrait), le résolveur va demander à un autre. Si une signature est invalide, le résolveur va en essayer d'autres. Cela part d'une bonne intention, éviter un échec des requêtes des clients. Mais cela ouvre la possibilité, pour un client malveillant, de donner beaucoup de travail au résolveur.
Le résolveur va essayer, pour chaque signature trouvée, toutes les clés cryptographiques présentes. En temps normal, il n'y a qu'une signature et une clé. Lors d'opérations comme le remplacement d'une clé, il peut y en avoir deux. Mais, et c'est le point important, ces nombres sont contrôlés par l'attaquant. S'il envoie dix clés et dix signatures, le résolveur aura cent vérifications à faire. Et si l'attaquant arrive à faire tenir cent clés et cent signatures dans le message, il faudra dix mille vérifications… Elles retiendront le résolveur pendant longtemps, l'empêchant de répondre aux autres requêtes (voire le bloquant complètement si ces vérifications ne sont pas réellement parallélisées).
Voyons maintenant quelques détails pratiques. D'abord, le résolveur ne va pas tester toutes les clés mais uniquement toutes celles qui ont l'identificateur, le keytag indiqué dans la signature. Si on trouve cette signature :
% dig +multi +dnssec brisbane.now.weather.dyn.bortzmeyer.fr TXT … ;; ANSWER SECTION: brisbane.now.weather.dyn.bortzmeyer.fr. 1800 IN TXT "Brisbane" "Partly cloudy" "27.0 C" "precipitation 0.17 mm" "wind 11.2 km/h" "cloudiness 25 %" "humidity 70 %" brisbane.now.weather.dyn.bortzmeyer.fr. 1800 IN RRSIG TXT 8 6 1800 ( 20240323040000 20240318154000 63937 dyn.bortzmeyer.fr. gUXD/SnFywfzQVRstRH9t5k6DIGXLHUeuSgM8ShNfTUx …
Alors, il n'est pas nécessaire d'essayer toutes les clés mais seulement celle(s) qui ont un identificateur (keytag ou key id) de 63937 (il est indiqué dans la signature, après les dates). Ce système est censé limiter le travail du résolveur. Si un attaquant envoie cent clés, une seule sera utilisée. Mais, car il y a un mais, ce keytag ne fait que deux octets (donc les collisions sont relativement fréquentes) et surtout, ce n'est pas un condensat cryptographique sûr, comme par exemple SHA-256. Il est facile pour un attaquant de générer cent clés ayant le même keytag et celui-ci ne sert alors plus à rien, le malheureux résolveur doit essayer toutes les clés.
Autre problème, pour l'attaquant (qui veut évidemment faire le plus de mal possible), la taille de la réponse. En UDP, un client DNS typique ne va pas accepter des réponses de plus de 4 096 octets, voire souvent 1 460 octets. Comme l'attaquant veut fourrer le plus grand nombre possible de clés et de signatures dans sa réponse, il a intérêt à utiliser les algorithmes à courbes elliptiques, comme ECDSA, dont les clés et les signatures sont bien plus petites qu'avec RSA.
Maintenant, quelles sont les solutions ? Le rapport sur KeyTrap est plein de phrases boursouflées comme « Solving these issues fundamentally requires to reconsider the basics of the design philosophy of the Internet. » C'est évidemment faux. Il faut simplement limiter le nombre d'opérations et/ou le temps passé. C'est une nécessité général du DNS, bien antérieure à KeyTrap. Le RFC 1035, en 1987, disait déjà « The amount of work which a resolver will do in response to a client request must be limited to guard against errors in the database, such as circular CNAME references, and operational problems, such as network partition which prevents the resolver from accessing the name servers it needs. While local limits on the number of times a resolver will retransmit a particular query to a particular name server address are essential, the resolver should have a global per-request counter to limit work on a single request. ». L'oubli de ce paragraphe a déjà mené à des possibilités d'attaque par déni de service comme l'attaque infinie, iDNS. La solution est donc évidente, limiter le nombre de vérifications. Il y a une part d'arbitraire dans cette limite (stopper au bout de trois clés ? de quatre ?) mais il est clair qu'il n'y a aucune raison légitime de vérifier cent clés. C'est la modification qui a été faite dans tous les résolveurs.
Noter que, pour l'exploitation de cette faille, il faut pouvoir parler au résolveur, pour lui demander de résoudre un nom dans le domaine contrôlé par l'attaquant, dommaine qu'il aura rempli de clés et de signatures. C'est évidemment facile pour un résolveur public, et pas trop difficile pour les gros FAI, mais plus compliqué pour des petits résolveurs locaux. Donc, tout résolveur validant était plus ou moins menacé. Ceci dit, tous les logiciels ont été patchés très vite et tous les administrateurs système sérieux ont déjà appliqué les patches.
Notez que cette attaque ne remet pas en cause l'importance de DNSSEC. Toute technique de sécurité peut être (plus ou moins facilement) détournée pour en faire une attaque par déni de service.
Quelques lectures :
.ru
a connu au même
moment une grande panne liée à une
collision de keytagsFirst publication of this article on 18 March 2024
On March 16 and 17 was the IETF hackathon in Brisbane. I worked on a DNSSEC feature called "compact denial of existence", and implemented it successfully (which was easy).
Compact Denial of Existence (CDE) was originally called "black lies". The name was changed, probably because of some typical US issue with every adjective of color, but also because there was a risk of confusion with lying DNS resolvers, which are something quite different. What is its purpose? For DNSSEC, you have to sign your records. You can do it offline, on a primary name server (the records and the signatures are then transferred to other authoritative name servers), or online, in every authoritative name server. The first method is more secure (you don't need the private key on every authoritative name server) and uses less CPU resources but it is not usable if you have very dynamic content, generated at every request. For this kind of content, you need online signing.
Online signing of records does not raise any particular issue. The difficulty starts with denial of existence, when you want to sign a statement that a name, or a specific record type does not exist. In that case, you have no data to sign. DNSSEC has several ways to deal with that and the simplest is NSEC records. They are records created to say "there is no name between this one and that one". Let's query a root name server to see these NSEC records:
% dig +dnssec @d.root-servers.net doesnotexist … ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 26586 … ;; AUTHORITY SECTION: doctor. 86400 IN NSEC dog. NS DS RRSIG NSEC doctor. 86400 IN RRSIG NSEC 8 1 86400 20240330170000 20240317160000 30903 . hZ08T19claSr8ZkU …
You get a NXDOMAIN (No Such Domain), obviously, and a NSEC record
(more than one, actually, but I make things simple, but see later),
which says that there is no TLD between .doctor
and
.dog
. This NSEC record is signed, proving that
.doesnotexist
does not exist.
When signing a complete zone, creating the NSEC records is easy. But if we generate DNS content dynamically, it is more tricky. The DNS server, when signing, does not always know the previous name and the next name, it may not have a complete view of the zone. A trick for dynamic signing was named "white lies" and documented in RFC 4470. The idea is to generate dynamically a NSEC with a previous name and a next name both very close from the requested name. Something like (this is a real domain, you can try it yourself):
% dig +multi +dnssec +noidnout doesnotexist.dyn.bortzmeyer.fr … ;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 61681 … ;; AUTHORITY SECTION: ~.doesnotexiss~.dyn.bortzmeyer.fr. 0 IN NSEC doesnotexist!.dyn.bortzmeyer.fr. RRSIG NSEC ~.doesnotexiss~.dyn.bortzmeyer.fr. 0 IN RRSIG NSEC 8 5 0 ( …
The names ~.doesnotexiss~.dyn.bortzmeyer.fr
and
doesnotexist!.dyn.bortzmeyer.fr
were choosen to
be close enough of the requested name. This is not the exact
algorithm of RFC 4470 because I had problems with some
resolvers but the general idea is the same. (Also,
+noidnout
was added because dig has some ideas
about what is a displayable domain name. There are no
IDN involved.) Here is how Drink, the
dynamic name server used in the hackathon, written in
Elixir, did the
"white lies" trick:
previous = Drink.Dnssec.previous_name(domain) nsec = %DNS.Resource{class: :in, domain: to_charlist(DomainName.name(previous)), type: @nsec, data: Drink.Dnssec.nsec(domain, is_base)}
Now, what is the problem with this way of generating dynamic NSEC records? I said before that you actually need two (and sometimes three) NSEC records because you also need to prove that the name could not have been generated by a wildcard. Each NSEC will require computation for its signature and, since all of this is done in real-time, we wish to minimize the number of computations (and the size of the response). Hence the Compact Denial of Existence (CDE, formerly known as "black lies"). The idea is to have a NSEC record which does not go from "a name just before" to "a name just after" but from "the requested name" to "a name just after". This way, you no longer need to prove there was no wildcard since you do not claim the name does not exist. (And this could be called a lie, but, since it is done by the authoritative name server which, by definition, tells the truth, the term is better left unused.) But it has an important consequence, the response code must now be NOERROR and not NXDOMAIN, obscuring the fact that the name does not "really" exist (something that some people at the IETF did not like). Here is the result, as produced by Cloudflare name servers:
% dig +multi +dnssec @ns4.cloudflare.com doesnotexist.cloudflare.com … ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 43367 … ;; AUTHORITY SECTION: doesnotexist.cloudflare.com. 300 IN NSEC \000.doesnotexist.cloudflare.com. RRSIG NSEC TYPE65283 cloudflare.com. 300 IN RRSIG SOA 13 2 300 ( …
As you can see, the left-hand part of the record (the "owner name",
doesnotexist.cloudflare.com
) is now set to the
requested name. And the return code is NOERROR, with no data
returned in the Answer section. The response says that the name
exists, but only with RRSIG, NSEC and TYPE65283 - more on this one
later - records.
It should be noted that any ordinary, unmodified, validating resolver will have no problem with this response. It will be validated. CDE does not require any change to the resolver. Also, checking tools like DNSviz and Zonemaster will see no problem. CDE can be deployed unilaterally, and this is exactly what Cloudflare did.
So, as we saw, CDE is not new, even if it is not described in any
RFC. It is
already deployed (remember that the Internet is permissionless: you
do not need a RFC to implement and deploy a technology). But what
IETF
is currently working one is precisely a specification of this
technique. The project is currently an
Internet-Draft, draft-ietf-dnsop-compact-denial-of-existence
. This
draft describes the basic technique for generating the unique NSEC
record and adds:
During the IETF hackathon, two authoritative name servers were modified to generate CDE:
compact-denial
(if you retrieved the code with git,
git diff -u -r master -r compact-denial
will
give you the code written during the hackathon).In both cases, the work was simple and fast: once you have dynamic signing (it was already in Drink), CDE is simpler than white lies. The two servers also implemented the NXNAME reporting and accept the signaling via the CO bit. Here is the result with Drink (this test domain was created for the hackathon, and no longer works):
% dig +dnssec +multi nonono.ietf.bortzmeyer.fr TXT … ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 49495 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 4, ADDITIONAL: 1 … nonono.ietf.bortzmeyer.fr. 0 IN NSEC \000.nonono.ietf.bortzmeyer.fr. \ RRSIG NSEC TYPE65283 nonono.ietf.bortzmeyer.fr. 0 IN RRSIG NSEC 8 4 0 ( 20240321040000 20240316154000 43304 ietf.bortzmeyer.fr. … …
As of today, no real resolver has been modified to implement NXDOMAIN restoration and/or EDNS wignaling with CO. This is not mandatory, but would be cool.
As usual, the hackathon pinpointed some issues that were not always noticed before:
Thanks to Shumon Huque for code, conversation and explanations.
Auteur(s) du livre : Ouvrage collectif
Éditeur : Fédération de la Libre Pensée
978-2-916801-23-0
Publié en 2023
Première rédaction de cet article le 22 février 2024
En France en 2024, le discours politique sur le monde arabo-musulman est souvent fondé sur une assignation identitaire, qui suppose que toute personne élevée dans un milieu musulman est forcément croyante, voire bigote, ou même islamiste. La réalité est différente et le monde arabo-musulman a lui aussi ses libres penseurs qui n'acceptent pas aveuglément les dogmes religieux, et encore moins les humains qui les utilisent pour assoir leur pouvoir. Cet ouvrage rassemble un certain nombre de contributions, très diverses, sur des libres penseurs dans l'histoire du monde arabo-musulman.
Tout le monde a entendu parler d'Omar Khayyam, bien sûr. Mais la plupart des autres libres penseurs décrits dans l'ouvrage sont nettement moins connus en Occident. Ils ne forment pas un groupe homogène : certains sont athées, d'autres simplement méfiants vis-à-vis de la religion organisée. Il faut aussi noter que le livre couvre treize siècles d'histoire et une aire géographique très étendue ; il n'est pas étonnant que la diversité de ces libres penseurs soit grande. Le livre contient aussi des textes qui remettent en perspective ces penseurs, par exemple en détaillant le mouvement de la Nahda ou bien en critiquant le mythe de la tolérance religieuse qui aurait régné en al-Andalus.
Le livre est assez décousu, vu la variété des contributeurs. On va de textes plutôt universitaires à des articles davantage militants (et certains sont assez mal écrits, je trouve, et auraient bénéficié d'une meilleure relecture). Mais tous ont le même mérite : tirer de l'oubli des libres penseurs souvent invisibilisés comme Tahar Haddad ou Ibn al-Rawandi. Et vous apprendrez de toute façon plein de choses (je ne connaissais pas le mutazilisme).
Auteur(s) du livre : Ouvrage collectif
Éditeur : Les presses de SciencesPo
978-2-7246-4150-9
Publié en 2023
Première rédaction de cet article le 21 février 2024
Cet atlas illustre (au sens propre) un certain nombre d'aspects du monde numérique (plutôt axé sur l'Internet). Beaucoup de données et d'infographies sous les angles économiques, sociaux et politiques.
L'ouvrage est très graphique, et riche. Tout le monde y trouvera des choses qui l'intéressent comme le spectaculaire tableau des systèmes d'exploitation p. 18 ou bien la représentation astucieuse de l'alternance des modes en IA p. 33 ou encore la visualisation de la censure en Russie p. 104 (tirée des travaux de Resistic). Les résultats ne sont pas forcément surprenants (p. 61, les youtubeurs les plus populaires sur les sujets de la voiture ou de la science sont des hommes, sur les sujet animaux, style et vie quotidienne, ce sont des femmes). Mais plusieurs articles mettent aussi en cause certaines analyses traditionnelles comme p. 82 celui qui dégonfle le mythe « fake news » (ou plus exactement leur instrumentalisation par les détenteurs de la parole officielle).
Les utilisations de l'Internet (en pratique, surtout les réseaux sociaux centralisés) sont très détaillées, par contre l'infrastructure logicielle est absente (la matérielle est présente via des cartes de câbles sous-marins et des articles sur l'empreinte environnementale).
Les infographies sont magnifiques mais souvent difficiles à lire, en raison de leur petite taille (sur la version papier de l'ouvrage) et du fait que chacune est le résultat de choix de présentation différents. D'autre part, les couleurs sont parfois difficiles à distinguer (p. 31 par exemple). Et l'axe des X ou celui des Y manque parfois, comme dans l'article sur la viralité p. 56 qui n'a pas d'ordonnées. Il faut dire que certaines réalités sont très difficiles à capturer comme la gouvernance de l'Internet p. 101, sujet tellement complexe qu'il n'est pas sûr qu'il aurait été possible de faire mieux.
Plus gênantes sont les bogues comme p. 31 la Quadrature du Net et FFDN classées dans « Secteur économique et start-ups ». Ou bien p. 37 LREM considéré comme plus à gauche que le PS…
Auteur(s) du livre : David Grann
Éditeur : Simon & Schuster
976-1-47118367-6
Publié en 2023
Première rédaction de cet article le 20 février 2024
Si vous aimez les histoires de bateau à voile, avec naufrages, mutineries, iles désertes et tout ça, ce roman vous emballera. Sauf que ce n'est pas un roman, le navire « The Wager » a vraiment existé et vraiment fait vivre toutes ces aventures à son équipage.
Le livre a été écrit par David Grann, connu comme l'auteur de « Killers of the flower moon ». En attendant que Scorsese en fasse un film, comme il l'a fait de « Killers of the flower moon », je recommande fortement la lecture du livre. « The Wager », navire de guerre anglais, n'a eu que des malheurs pendant la guerre de l’oreille de Jenkins. Départ retardé, épidémies à bord alors même qu'il était encore à quai, problèmes divers en route, une bonne partie de l'équipage mort alors même que le Wager n'a pas échangé un seul coup de canon avec les Espagnols. Le bateau finit par faire naufrage en Patagonie. À cette époque, sans radio, sans GPS, sans même de mesure de longitude correcte, de tels naufrages n'étaient pas rares. (Le livre parle aussi d'une escadre espagnole qui a tenté d'intercepter celle où se trouvait le Wager, et n'a pas été plus heureuse.)
Ce qui est plus original dans le cas du Wager est que des survivants sont rentrés séparement en Angleterre et qu'ils ont raconté des histoires très différentes sur le naufrage et la mutinerie qui a suivi. Chacun (en tout cas ceux qui savaient écrire ou bien trouvaient quelqu'un pour le faire pour eux) a publié sa version et l'auteur de ce livre essaie de rendre compte des différents points de vue.
Donc, pas un roman mais autant de rebondissements que dans un roman. Vous feriez comment, vous, pour rentrer en Angleterre si vous étiez coincé sur une des iles les plus inhospitalières d'une des régions les plus inhospitalières du monde ? Le plus extraordinaire est que plusieurs groupes de survivants y sont arrivés, par des moyens différents.
Sinon, les informaticien·nes noteront que l'arrière-grand-père d'Ada Lovelace était à bord (il fait partie de ceux qui ont écrit un livre sur le voyage du Wager). Les fans de Patricio Guzmán verront que cela se passe au même endroit que son film « Le bouton de nacre » (et on croise dans le livre les mêmes Kawésqars).
Auteur(s) du livre : Ouvrage collectif
Éditeur : Les éditions de l'atelier
978-2-7082-5410-7
Publié en 2023
Première rédaction de cet article le 19 février 2024
Comme les lecteurices de ce blog le savent sûrement, je pense que la place prise par le numérique dans nos vies justifie qu'on réfléchisse sérieusement à cette transition que nous vivons. Donc, ce livre est une bonne idée. Mais, comme beaucoup d'ouvrages collectifs, il souffre d'un manque évident de coordination entre les auteurices, dont les différentes contributions sont de niveaux très inégaux.
On y croise ainsi des baratineurs classiques comme Luc Ferry, personnage qu'on aurait pu penser définitivement démonétisé. Sans surprise, il parle d'un sujet cliché, le transhumanisme (le mannequin de paille classique de beaucoup d'« intellectuels » français). Sans aller aussi loin dans la vacuité, on trouve aussi dans ce livre des articles par des auteurs dont on peut se demander s'ils maitrisent leur sujet. Ainsi une auteure présentée comme spécialiste des questions bancaires déplore que l'IA a des biais quand elle décide d'accorder un prêt ou pas (un exemple, typique de la sur-utilisation de la notion de biais ; évidemment qu'un outil d'aide à la décision en matière de prêts va être mal disposé envers les gens ayant des revenus faibles et irréguliers). Le texte de présentation de chaque contribution a aussi sa part de responsabilité, comme celui de l'article sur la chaine de blocs qui dit que les données sont cryptées (et, non, le problème n'est pas l'utilisation de cet terme, « chiffrées » n'aurait pas été mieux). Enfin lisant un livre qui se veut de réflexion sur le numérique, je vois la phrase « les technologies digitales sont des technologies qui sont faciles à prendre en main », et je me demande si l'auteur l'a fait exprès.
Bon, assez critiqué, il y a aussi des analyses intéressantes (j'ai dit intéressantes, pas forcément que j'étais 100 % d'accord) : Pauline Türk réfléchit sur l'utilisation du numérique pour améliorer le processus démocratique, Ophélie Coelho traite de géopolitique et détaille les risques liés à la dépendance vis-à-vis d'entreprises ou d'États, Lionel Maurel plaide pour l'ouverture des résultats des recherches scientifiques, mais aussi (avec nuances) pour l'ouverture des données de recherche, Philippe Bihouix explique concrètement comment réduire l'empreinte environnementale, Jessica Eynard questionne l'identité numérique (même si je la trouve bien trop optimiste sur le projet d'identité numérique étatique au niveau de l'UE), Amine Smihi décrit la vidéosurveillance vue de son rôle d'élu local, etc.
Bref, un tour d'horizon de beaucoup de questions que pose la place du numérique dans notre société.
Première rédaction de cet article le 30 janvier 2024
Dernière mise à jour le 8 février 2024
Aujourd'hui, une panne a affecté le domaine de premier
niveau russe,
.ru
. Que sait-on ?
(Quand cet article a été initialement publié, on ne savait pas
exactement ce qui s'était passé. Voir à la fin pour des explications vraisemblables.)
La panne a duré à peu près de 15:20 UTC à
19:00 UTC (avec une réparation partielle à 17:50 UTC).
Pour l'instant, il semble certain que le problème était lié à
DNSSEC et qu'il s'agissait probablement
d'une panne, pas d'une action volontaire de la Russie ou de ses
ennemis (hypothèse parfois citée, vu le contexte de
guerre). Un petit rappel : DNSSEC est une technique de
sécurité visant à garantir l'intégrité des noms de domaine. DNSSEC, comme toutes les
techniques de sécurité, peut, en cas de fausse manœuvre, aboutir à
un déni de service ; si vous perdez les clés de votre maison, vous
ne pourrez plus rentrer chez vous. Ici, les signatures des domaines de
.ru
étaient invalides,
menant les résolveurs DNS à les
rejeter et donc à ne pas réussir à résoudre les noms de domaine sous
.ru
. Deux points sont notamment à noter :
.ru
affecte tous les
noms situés sous .ru
. La gestion d'un
TLD est
quelque chose de critique ! Il est donc faux de dire que tel ou
tel site Web russe était en panne ; en fait, son nom de domaine ne
fonctionnait plus.Compte-tenu des observations faites sur le moment, il semble bien
que le communiqué du
ministère russe était correct (à un point près, détaillé plus
loin). Le problème était bien dans le
registre du .ru
et était
sans doute le résultat d'une erreur de leur côté. La traduction du
communiqué par DeepL : « Ministère russe de
la numérisation \ L'accès aux sites de la zone .RU sera bientôt
rétabli \ La zone .RU a été affectée par un problème technique lié à
l'infrastructure DNSSEC* mondiale. Des spécialistes du Centre
technique de l'internet et de MSC-IX travaillent à son
élimination. \ Actuellement, le problème a été résolu pour les
abonnés du système national de noms de domaine. Les travaux de
restauration sont en cours. Nous vous tiendrons au courant de
l'évolution de la situation. \ *DNSSEC est un ensemble d'extensions
du protocole DNS, grâce auxquelles l'intégrité et la fiabilité des
données sont garanties. » Et le communiqué original sur
Telegram :
Notons que les TLD
.su
et
.рф
ne semblent pas
avoir été affectés, alors qu'ils sont gérés par le même registre.
Un peu plus de technique, maintenant. Avec dig, voyons la résolution DNS, via un résolveur validant :
% date -u ; dig ru. NS Tue 30 Jan 17:12:05 UTC 2024 ; <<>> DiG 9.18.18-0ubuntu0.22.04.1-Ubuntu <<>> ru. NS ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: SERVFAIL, id: 32950 ;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 1232 ; EDE: 6 (DNSSEC Bogus): (NXJA) ;; QUESTION SECTION: ;ru. IN NS ;; Query time: 2384 msec ;; SERVER: 192.168.2.254#53(192.168.2.254) (UDP) ;; WHEN: Tue Jan 30 18:12:07 CET 2024 ;; MSG SIZE rcvd: 41
Le SERVFAIL indique un échec. Notez l'EDE (Extended DNS Error, RFC 8914), qui indique que l'échec est lié à DNSSEC.
Ensuite, vérifions que tout
marchait, à part DNSSEC. En coupant la validation
DNSSEC (option +cd
, Checking
Disabled), on avait bien une réponse :
% date -u ; dig +cd ru. NS Tue 30 Jan 17:15:06 UTC 2024 ; <<>> DiG 9.18.18-0ubuntu0.22.04.1-Ubuntu <<>> +cd ru. NS ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5673 ;; flags: qr rd ra cd; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 1232 ;; QUESTION SECTION: ;ru. IN NS ;; ANSWER SECTION: ru. 344162 IN NS b.dns.ripn.net. ru. 344162 IN NS d.dns.ripn.net. ru. 344162 IN NS e.dns.ripn.net. ru. 344162 IN NS f.dns.ripn.net. ru. 344162 IN NS a.dns.ripn.net. ru. 344162 IN RRSIG NS 8 1 345600 ( 20240305102631 20240130141847 52263 ru. kw9oqgvi/l0MZp/6FY0Ha+VZDWRR3+iDUCYqAY5W7D2w CfIXXdOOvdd58nNY7z/3b4fRK6tlTF3wQpCDSpeKrmkW mric4kcUptaj1rp71lC0GHXHmGwDsx8Zi/lvo6sJEk0g uBbJYBJkzKqeseD4u1Pw29jkRHhQEJKk2seP+Zk= ) ;; Query time: 8 msec ;; SERVER: 192.168.2.254#53(192.168.2.254) (UDP) ;; WHEN: Tue Jan 30 18:15:06 CET 2024 ;; MSG SIZE rcvd: 285
Les serveurs de nom faisant autorité répondaient bien :
% dig @b.dns.ripn.net. ru NS ; <<>> DiG 9.18.18-0ubuntu0.22.04.1-Ubuntu <<>> @b.dns.ripn.net. ru NS ; (2 servers found) ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37230 ;; flags: qr aa rd; QUERY: 1, ANSWER: 6, AUTHORITY: 0, ADDITIONAL: 11 ;; WARNING: recursion requested but not available ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;ru. IN NS ;; ANSWER SECTION: RU. 345600 IN NS a.dns.ripn.net. RU. 345600 IN NS e.dns.ripn.net. RU. 345600 IN NS b.dns.ripn.net. RU. 345600 IN NS f.dns.ripn.net. RU. 345600 IN NS d.dns.ripn.net. ru. 345600 IN RRSIG NS 8 1 345600 ( 20240305102631 20240130141847 52263 ru. kw9oqgvi/l0MZp/6FY0Ha+VZDWRR3+iDUCYqAY5W7D2w CfIXXdOOvdd58nNY7z/3b4fRK6tlTF3wQpCDSpeKrmkW mric4kcUptaj1rp71lC0GHXHmGwDsx8Zi/lvo6sJEk0g uBbJYBJkzKqeseD4u1Pw29jkRHhQEJKk2seP+Zk= ) ;; ADDITIONAL SECTION: a.dns.RIPN.net. 1800 IN AAAA 2001:678:17:0:193:232:128:6 b.dns.RIPN.net. 1800 IN AAAA 2001:678:16:0:194:85:252:62 d.dns.RIPN.net. 1800 IN AAAA 2001:678:18:0:194:190:124:17 e.dns.RIPN.net. 1800 IN AAAA 2001:678:15:0:193:232:142:17 f.dns.RIPN.net. 1800 IN AAAA 2001:678:14:0:193:232:156:17 a.dns.RIPN.net. 1800 IN A 193.232.128.6 b.dns.RIPN.net. 1800 IN A 194.85.252.62 d.dns.RIPN.net. 1800 IN A 194.190.124.17 e.dns.RIPN.net. 1800 IN A 193.232.142.17 f.dns.RIPN.net. 1800 IN A 193.232.156.17 ;; Query time: 268 msec ;; SERVER: 2001:678:16:0:194:85:252:62#53(b.dns.ripn.net.) (UDP) ;; WHEN: Tue Jan 30 17:56:45 CET 2024 ;; MSG SIZE rcvd: 526
Comme le note DNSviz, les signatures étaient invalides :
Il ne s'agissait pas de problèmes locaux. En demandant aux sondes RIPE Atlas, on voit beaucoup d'échecs de résolution (SERVFAIL, SERVer FAILure) :
% blaeu-resolve -r 200 --type NS ru [ERROR: SERVFAIL] : 79 occurrences [a.dns.ripn.net. b.dns.ripn.net. d.dns.ripn.net. e.dns.ripn.net. f.dns.ripn.net.] : 66 occurrences [ERROR: NXDOMAIN] : 13 occurrences Test #66905129 done at 2024-01-30T16:59:57Z
(Notez les NXDOMAIN - No Such Domain, qui sont des mensonges de résolveurs qui censurent la Russie, en raison de son agression de l'Ukraine.)
Une fois que tout était réparé, la validation se passait bien :
% blaeu-resolve -r 200 --type DNSKEY --displayvalidation --ednssize 1450 ru … [ERROR: NXDOMAIN] : 19 occurrences [ (Authentic Data flag) 256 3 8 awea…] : 93 occurrences [256 3 8 aweaa…] : 64 occurrences [ERROR: SERVFAIL] : 9 occurrences [] : 1 occurrences [ (TRUNCATED - EDNS buffer size was 1450 ) ] : 1 occurrences Test #66910201 done at 2024-01-30T18:20:56Z
Notez que la phrase du ministre « le problème a été résolu pour les abonnés du système national de noms de domaine » était fausse. Le problème a été résolu pour tout le monde (ce « système national de noms de domaine » est l'objet de beaucoup de propagande et de fantasme et il n'est pas du tout sûr qu'il ait une existence réelle). La situation actuelle :
% dig ru DNSKEY ; <<>> DiG 9.18.18-0ubuntu0.22.04.1-Ubuntu <<>> ru DNSKEY ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6401 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 1232 ;; QUESTION SECTION: ;ru. IN DNSKEY ;; ANSWER SECTION: ru. 39023 IN DNSKEY 256 3 8 ( AwEAAcBtr/w2hP6OQjiCacPFzK6xh0pR7QsHV9lxprIX G9WBoBB5XWPVc5q17F3yt3wpJ7xmedt80gxVMaicPYNY Aa8YUFyMxTGVBDVQlz5gCmXQKlr0yImI78sdwzWNmgKH ap0BLypTBVxAKxpcvuwTmqXQUSONkjq9werHvogrvkUb ) ; ZSK; alg = RSASHA256 ; key id = 44301 ru. 39023 IN DNSKEY 257 3 8 ( AwEAAfcMZekGvFYkmt18Q8KIS+XX/7UBbpmIJ4hK3FNz ErkJmiNPxq+sbM00NYJwY725QxoDwHPeK0JIybKW6s+U 2+I7aBGJus/bvDklw0CMTDsG7HoJG+4sjq/jRPUQNwkO A/cNoiYjroqw7/GnB8DEAGE03gyxdZcxxU3BJKPZfds8 DJYPBaDUO35g9I6+dLPXxHK6LFUFprkBtpqj13tJ/ptL yMSaivvi3xrvJMqu/FWN6piMzu7uYmrSdv6s01y+0x62 29sZ7ufSQ6E66gdTmXebDx8S8q70B4BmMZosrsHlf3uX VMMY72LnrQzbere2ecd95q+1VDMDXcXB/pT1/C0= ) ; KSK; alg = RSASHA256 ; key id = 43786 ru. 39023 IN DNSKEY 256 3 8 ( AwEAAbjj3GP0TUwaNI7BIIw/fvwKTmdR+oZsEPk64pl8 VYn4dfdbGHWpYIooxcgEbuBEYrnC/oqnKhad38nTxrZ9 SAK3qV5qShntFdgozS5IKs5m1ebNmg2sotlhXRhJ4vqg H+BQh/lw6T4vdB8FE5tHGE1vwPs9Vhj0vLTBhX8TwB6/ ) ; ZSK; alg = RSASHA256 ; key id = 52263 ru. 39023 IN RRSIG DNSKEY 8 1 345600 ( 20240213000000 20240124000000 43786 ru. rgqGAFvlWoGzSGrKaaMUqpNkOGyKQS+MOwvrwy3+A4nh FiioZ610H9GlN/fh3kUjiFrRJ7T1sKiW9AVekkpdZk/Q T5vRGqWi+PLyuRtfL7ZtJKVgUPV+jGVOc0A9AZA0dVgx qX54L+mbMY6OGCcMeTHwUpg6J2UR0MmB3TCyHPhg0Z/L VHWf/E9hHO4dqdePwKvlVeA7txcXemiJE6C1/mgFvtQl uQsot9eDOqKZt9oUpqzi63gsw835+h6fiKNbMJf4SEPw 5enbdQqcubSWwwY/SbeoW74qgPgPgJrmiP8UxT6DEAZc W5UO9CsMcyfgifsL0zy5SMba4kS0izp4rQ== ) ;; Query time: 4 msec ;; SERVER: 192.168.2.254#53(192.168.2.254) (UDP) ;; WHEN: Tue Jan 30 21:04:05 CET 2024 ;; MSG SIZE rcvd: 893
Plusieurs personnes ont noté une lenteur, ou même parfois une
absence de réponse, de la part de certains des serveurs faisant
autorité pour .ru
. Il s'agit
probablement d'un effet dû aux efforts des résolveurs DNS qui, recevant des
signatures invalides, réessayaient (éventuellement vers d'autres
serveurs faisant autorité) dans l'espoir de récupérer enfin des
signatures correctes. En tout cas, rien n'indique qu'il y ait eu une
attaque externe contre .ru
.
Mais peut-on savoir ce qui s'est passé ? Pas exactement, mais on peut
toujours poursuivre l'analyse. DNSviz nous donne également accès
aux numéros de série, stockés dans l'enregistrement SOA de la zone
.ru
. On peut donc reconstituer une
chronologie (elle a été faite par Phil Kulin et vérifiée par moi) :
Enfin, on notera que le numéro de série ne bougeait plus (il changeait toutes les deux heures environ auparavant), ce qui fait penser que le problème n'était pas tant dans la clé 52263 que dans un système de signature désormais cassé.
% dig ru SOA ; <<>> DiG 9.18.19-1~deb12u1-Debian <<>> ru SOA ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 44285 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 6, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;ru. IN SOA ;; ANSWER SECTION: ru. 86400 IN SOA a.dns.ripn.net. hostmaster.ripn.net. ( 4058856 ; serial 86400 ; refresh (1 day) 14400 ; retry (4 hours) 2592000 ; expire (4 weeks 2 days) 3600 ; minimum (1 hour) ) ru. 86400 IN RRSIG SOA 8 1 345600 ( 20240313163045 20240130121923 44301 ru. dAGkfXQSmGCwwUuzxIfNeIgd+/BbAYt0whh3JcqvKi6x z6N8a7KGBHkfBCbg19xzx6b+LQBJgK4yVtvTQLqLNo8P fxg5J8S/JUw2EHgPUMJw0CjsrqH85biJqkv+5TVoN9dG PFnFIjaTPLP0DRscR3ps5NP8lJstDwBYQmg/i68= ) ;; AUTHORITY SECTION: ru. 86400 IN NS e.dns.ripn.net. ru. 86400 IN NS d.dns.ripn.net. ru. 86400 IN NS a.dns.ripn.net. ru. 86400 IN NS f.dns.ripn.net. ru. 86400 IN NS b.dns.ripn.net. ru. 86400 IN RRSIG NS 8 1 345600 ( 20240304113906 20240126101933 44301 ru. KthCG9ahQ3UyFl1aakpJRiXI0GXH6TNB6i+uY+920a93 DQgCgkokpsYAHCCzqJl0VXiAmcaEK1yLFHxfJzDbjsel 0xz8IJi3CIuzEtBfBbedXUfBzE/64HmJ9xHVgc5fdLDA 6AfIAmw0oeHCgssUTdZ67IlZO90nzeEHu6PHj2k= ) ;; Query time: 32 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP) ;; WHEN: Wed Jan 31 10:45:15 CET 2024 ;; MSG SIZE rcvd: 580
Il n'a repris sa progression que dans la journée du 31 janvier, et la clé 52263 était à nouveau utilisée :
% dig ru SOA ; <<>> DiG 9.18.19-1~deb12u1-Debian <<>> ru SOA ;; global options: +cmd ;; Got answer: ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 37102 ;; flags: qr rd ra ad; QUERY: 1, ANSWER: 2, AUTHORITY: 6, ADDITIONAL: 1 ;; OPT PSEUDOSECTION: ; EDNS: version: 0, flags: do; udp: 4096 ;; QUESTION SECTION: ;ru. IN SOA ;; ANSWER SECTION: ru. 86397 IN SOA a.dns.ripn.net. hostmaster.ripn.net. ( 4058871 ; serial 86400 ; refresh (1 day) 14400 ; retry (4 hours) 2592000 ; expire (4 weeks 2 days) 3600 ; minimum (1 hour) ) ru. 86397 IN RRSIG SOA 8 1 345600 ( 20240306211526 20240201082001 52263 ru. trZgMG6foXNX+ZotfA11sPGSCJwVpdSzobvrPbMfagIj pToI2+W9fa3HIW5L3GSliQHbWDnaTp0+dhMs/v3UFnFs UtCoTy00F/d1FysBtQP2uPZLwPTI3rXJSE2U5/Xxout/ 2hSCsIxQE5CxksPzb9bazp63Py0AfbWY56b1/FE= ) ;; AUTHORITY SECTION: ru. 86397 IN NS e.dns.ripn.net. ru. 86397 IN NS f.dns.ripn.net. ru. 86397 IN NS a.dns.ripn.net. ru. 86397 IN NS b.dns.ripn.net. ru. 86397 IN NS d.dns.ripn.net. ru. 86397 IN RRSIG NS 8 1 345600 ( 20240303133621 20240131134337 52263 ru. MD8EOMQtjhr08qt3I890qHE+E0HBvhdbtUkasjez+1zd 8zsxH0GCPz5zD0db/HQr9o0hDUMd3xZLHaDvlS/PjLti 6dEVOT7SYHHC2yF7Ypu97alFpEHpGEcchEhMx3rSUBZF Jik3JVG9yqOxF4m0r+QgVotU4PMIejFGjPdvZ0w= ) ;; Query time: 16 msec ;; SERVER: 127.0.0.1#53(127.0.0.1) (UDP) ;; WHEN: Thu Feb 01 11:27:06 CET 2024 ;; MSG SIZE rcvd: 580
Merci à John Shaft, et Paul Ó Duḃṫaiġ pour les analyses expertes et les discussions. J'ai fait à la réunion FOSDEM du 3 février 2024 un exposé rapide sur la question (supports et vidéo sont en ligne).
Vous pouvez lire le premier communiqué officiel du registre (dont les affirmations sont conformes aux observations). Contrairement à ce qui a été affirmé sans preuves, il n'y a aucune indication que cette panne soit liée aux projets russes de renforcement du contrôle étatique sur la partie russe de l'Internet.
L'explication technique officielle et détaillée a finalement été
donnée le 7 février dans un message aux
BE. Il s'agirait donc bien d'une bogue
logicielle dans le système de signature, qui se déclenchait lorsque
deux clés avaient le même key tag (ce qui est
possible, vu la taille de ce key tag, mais
n'aurait pas dû provoquer de faille.) Cette explication officielle
colle très bien aux observations qui ont été faites au moment de la
panne. Notez aussi que cette explication confirme bien que
.рф
et .su
n'ont
pas été affectés (conformément à ce qui a été observé, et au
contraire de ce qu'ont affirmé plusieurs articles écrits sans
vérifier) alors que deux TLD peu connus,
.дети
et .tatar
l'ont été.
Première rédaction de cet article le 29 janvier 2024
Je viens de me lancer dans l'utilisation d'une clé de sécurité (une Nitrokey) pour les protocoles d'authentification FIDO2 et WebAuthn. Regardons cela.
Un peu de contexte d'abord. Lorsqu'on veut s'authentifier auprès d'un service en ligne (un serveur SSH, un site Web, etc), la solution la plus ancienne est le couple identificateur (login) / mot de passe. Ce système pose plusieurs problèmes :
Tous ces problèmes ont des solutions. Par exemple, le premier se résout très bien par l'utilisation d'un gestionnaire de mots de passe, solution très recommandée. Mais, évidemment, aucune des solutions n'est parfaite.
On cherche donc à renforcer la sécurité, en ajoutant un deuxième facteur d'authentification, par exemple un code à usage restreint, généré sur une machine distincte, qui peut être un ordinateur (rappel : un ordiphone est un ordinateur) ou un périphérique matériel spécialisé, souvent nommé « clé ». Des protocoles normalisés existent comme HOTP (RFC 4226, le code est alors à usage unique) ou TOTP (RFC 6238, le code étant alors à usage limité dans le temps). Il existe aussi des solutions non normalisées, spécifiques à un seul vendeur, qu'il faut bien sûr fuir. Aujourd'hui, lorsqu'on veut accéder à un service sensible (l'interface de gestion de ses noms de domaine, par exemple), cette authentification à deux facteurs doit être considérée comme impérative.
Je vais essayer ici une technique qui permet, soit de remplacer le couple identificateur / mot de passe par une authentification à un seul facteur, mais plus sûre, soit d'utiliser comme deuxième facteur une méthode plus sûre que HOTP ou TOTP. Il s'agit de FIDO2/WebAuthn. C'est en fait un ensemble de plusieurs protocoles. Pour les expliquer, un peu de terminologie (tout en se rappelant qu'en français, il n'y a souvent pas de terminologie standard et répandue pour une bonne partie du vocabulaire de l'informatique). Il y a quatre entités, humaines et non-humaines, qui interviennent
Le protocole entre l'authentificateur (relayé par le logiciel client) et le service utilise la classique cryptographie asymétrique. Le service génère des données à usage unique qu'il chiffre avec la clé publique de l'authentificateur, celui-ci les déchiffrera et les enverra, prouvant ainsi qu'il connait la clé privée. Le service n'apprend donc jamais aucun secret (contrairement à ce qui se passe avec le mot de passe), ce qui protège contre le hameçonnage. Les clés privées ne quittent pas l'authentificateur, le logiciel client ne les voit jamais. FIDO2 désigne la communication avec l'authentificateur, WebAuthn l'API JavaScript exposée aux services.
Voyons en pratique ce que cela donne, avec le service de test
. Je visite
le site Web avec Firefox, j'indique mon identificateur puis je
clique sur Authenticate : https://webauthn.io/
Je sors ensuite ma clé, une Nitrokey 3A NFC :
Je la mets dans le port USB de la machine et j'appuie sur le bouton de la clé (une lumière sur la clé clignote pour vous le rappeler) et je suis authentifié. Ici, on n'a qu'un seul facteur (la possession de la clé). Avec la version de Firefox sur mon système, on ne peut pas ajouter de code PIN à la clé (mais ça marche avec Chrome, sur la même machine ; dans le monde FIDO2 / WebAuthn, vous rencontrerez souvent ce genre de problèmes d'interopérabilité ; sur Android, Fennec n'a tout simplement pas WebAuthn). Notez aussi que, la première fois, il faudra s'enregistrer auprès du service pour être reconnu par la suite.
La Nitrokey 3A NFC que je possède peut aussi, comme son nom l'indique, être « connectée » en NFC (testé avec succès sur mon Fairphone, bien qu'il faille un peu chercher où la placer sur l'ordiphone, et avec un Samsung, même problème).
Une fois que vous avez testé votre clé sur ce service et que tout marche, vous pouvez commencer à activer son utilisation sur divers services réels (mais lisez cet article jusqu'au bout : il y a des pièges). Ici, par exemple, sur Proton Mail :
Sans la configuration d'un code PIN sur la clé, le vol de la clé permet au voleur de se faire passer pour vous. Il ne faut donc l'utiliser qu'en combinaison avec un autre facteur. Certaines clés, plus coûteuses, peuvent reconnaitre leur utilisateurice par biométrie mais ne vous fiez pas aux promesses des commerciaux : la biométrie est très loin d'être la solution miracle. Non seulement un attaquant peut reproduire des caractéristiques biométriques (vous laissez vos empreintes digitales un peu partout) mais inversement, vos propres doigts ne sont pas forcément reconnus (les empreintes s'usent avec l'âge et vous aurez de plus en plus de problèmes).
Quand à la perte de la clé, elle doit être anticipée. Sans la clé, vous ne pourrez plus vous connecter. Comme le dit la FAQ, il faut avoir un plan B. Par exemple, lorsqu'on active la sécurité FIDO2/WebAuthn sur Cloudflare, le service fournit une longue série de chiffres secrets qui permettront de récupérer son compte, et suggère très fortement de les stocker dans un endroit sécurisé (à la fois contre le vol et contre la perte). Il n'y a pas de mécanisme de sauvegarde de la clé (c'est délicat à concevoir pour un dispositif dont le but est d'empêcher les clés privées de sortir). La solution la plus simple semble être d'avoir deux clés et d'enregistrer les deux auprès des services qui le permettent.
Pour gérer ma clé, j'utilise l'utilitaire en ligne de
commande nitropy. La
documentation en ligne n'est pas terrible (sauf pour la partie sur
l'installation sur une machine Linux, où il
faut configurer
udev). Mais le logiciel a une aide avec
--help
. Voici quelques exemples d'utilisation :
% nitropy list Command line tool to interact with Nitrokey devices 0.4.45 :: 'Nitrokey FIDO2' keys :: 'Nitrokey Start' keys: :: 'Nitrokey 3' keys /dev/hidraw3: Nitrokey 3 522A… % nitropy nk3 status Command line tool to interact with Nitrokey devices 0.4.45 UUID: 522A… Firmware version: v1.5.0 Init status: ok Free blocks (int): 60 Free blocks (ext): 478 Variant: LPC55
Pour finir, des liens utiles :
Première rédaction de cet article le 8 janvier 2024
Je gère depuis longtemps mon propre serveur de courrier électronique. Cet article va présenter quelques observations issues de cette expérience. Si vous êtes pressé·e, voici un résumé : 1) ça marche et je peux envoyer du courrier y compris aux grosses plateformes 2) c'est compliqué et ça fait du travail.
Je commence par des explications générales, si vous vous intéressez aux détails techniques, ils sont à la fin. Si vous n'êtes pas familier·ère avec le monde du courrier électronique, quelques notions. Un élément essentiel du cahier des charges du courrier est la possibilité d'envoyer des messages non sollicités, à quelqu'un avec qui on n'a jamais échangé. Les cas d'usage de cette fonction sont nombreux (candidature spontanée à une entreprise, demande d'un devis à une entreprise, remarques sur un article de blog, etc.). Cet élément est une des raisons du succès du courrier électronique, qui reste aujourd'hui le principal moyen de contact universel. Mais il y a un côté négatif, le spam, puisque les spammeurs, eux aussi, utilisent cette possibilité pour nous envoyer leurs messages sans intérêt. La lutte contre le spam est un sujet complexe mais le point qui nous intéresse ici est que, comme pour toute question de sécurité, il y a forcément des faux négatifs (des coupables qui s'en tirent) et des faux positifs (des innocents frappés à tort). Ceux qui vous diraient le contraire et prétendraient qu'on peut avoir des solutions sans faux positifs ni faux négatifs sont des menteurs ou des commerciaux.
Des organisations qui gèrent des grandes plateformes de
courrier, comme Google avec
gmail.com
ou Microsoft
avec outlook.com
et
hotmail.com
, déploient diverses techniques pour
lutter contre le spam. Un effet de bord de ces techniques, dit-on
souvent, est de bloquer les petits hébergeurs ou les
auto-hébergeurs, les petites structures ou les particuliers qui,
comme moi, gèrent leur propre serveur. Une accusation courante est
qu'il ne s'agit pas de difficultés techniques, mais d'une politique
délibérée, que ces grandes entreprises essaient volontairement de
tuer cet auto-hébergement. Il n'y a pourtant aucune raison
technique, ni dans les normes techniques du
courrier électronique (RFC 5321 et RFC 5322), ni dans des règles légales, qui impose
que le courrier ne passe que par un oligopole de grandes
entreprises ; une personne ou une petite organisation peut
parfaitement gérer son propre serveur. Sans le spam, ce serait même
plutôt simple techniquement. Mais, avec le spam et ses conséquences,
les choses se compliquent.
(Point philosophico-politique au passage : la délinquance ou l'incivilité a des conséquences directes, si on vous vole votre vélo, vous n'avez plus de vélo mais aussi, et c'est trop souvent oublié, des conséquences indirectes et qui sont souvent plus coûteuses : déploiement de mesures de sécurité chères et gênantes, perte de confiance qui délite la société, etc.)
Voyons maintenant la légitimité des plateformes de courrier électronique à prendre des mesures anti-spam qui m'empêcheront parfois de joindre mon correspondant. Le gestionnaire d'un service de messagerie est maître chez lui. Il ou elle peut déployer les techniques qu'il veut, même si cela bloque certains émetteurs, de même que vous n'êtes pas obligés de prendre le tract qu'on vous tend dans une manifestation. Google n'est pas obligé d'accepter vos messages, rien ne l'y force. Toutefois, la situation n'est pas la même que celle de la personne qui ne veut pas prendre un tract. Ici, il y a un intermédiaire, qui va décider pour ses utilisateurs. Ceux-ci et celles-ci ne sont jamais informé·es de la politique de lutte anti-spam de leur hébergeur. Mieux, la propagande des grandes plateformes affirme en général que, si le message n'arrive pas, c'est de la faute de l'émetteur. Beaucoup d'utilisateurs ou d'utilisatrices croient sur parole ce discours.
J'ai ainsi été étonné de voir que des gens, ne recevant pas mes
messages, ne voulaient pas se plaindre auprès de leurs fournisseurs,
me demandant de le faire. Pourtant, n'étant pas client de
outlook.com
, j'avais certainement beaucoup
moins de poids sur Microsoft que des utilisateurs de leur
service.
(Si vous utilisez le fédivers comme réseau social, vous aurez noté que c'est pareil, et pour cause, le courrier électronique était un réseau social décentralisé bien avant le fédivers. C'est pareil car le gestionnaire d'une instance peut refuser qui il veut. En soi, ce n'est pas un problème, mais cela se complique quand le petit chef qui dirige une instance le fait pour le compte de ses utilisateurices.)
Donc, le petit hébergeur de courrier part avec un sérieux
désavantage : Google ou Microsoft se fichent pas mal de lui, et
leurs utilisateurs et utilisatrices font une confiance aveugle à ces
GAFA, davantage qu'à leurs propres
correspondant·es. Google, Microsoft, Orange,
laposte.net
, etc, se satisferaient certainement
d'un oligopole où tout se passerait entre eux. Heureusement, le
monde du courrier électronique n'en est pas là. On va voir qu'il
n'est pas exact de dire que les gros hébergeurs bloquent
déliberement les petits. D'une part, ils ne sont pas les seuls à
poser problème, d'autre part, ils ne sont pas acharnés à bloquer les
petits, c'est plutôt qu'ils s'en foutent.
(Au passage, les politiques anti-spam des grosses plateformes de courrier peuvent rejeter le message directement, ce qui donne à l'expéditeur un message d'erreur, ou bien escamoter le message sans avertissement, ou encore le mettre dans la boite Spam du destinataire. Je vais traiter tous ces cas ensemble.)
Parmi les problèmes que j'ai rencontrés, le fait d'être listé de temps en temps dans une liste de blocage de Spamhaus. Mon serveur n'a jamais envoyé de spam, il n'héberge même pas de liste de diffusion (héberger une liste est bien plus difficile, puisque, par construction, elles envoient en masse, ce que certains récepteurs considèrent comme du spam). La communication avec Spamhaus n'a jamais donné de résultat, notamment lorsque je leur ai demandé les spams que j'avais soi-disant envoyés, pour que je puisse les étudier et comprendre un éventuel problème (jamais de réponse). Le rejet du courrier via l'utilisation de cette liste est loin d'être spécifique aux gros méchants GAFA. Pas mal de libristes utilisent la liste de Spamhaus et se montrent assez agressifs quand on leur suggère que c'est peut-être une mauvaise idée (Spamhaus a beaucoup de fanboys).
Autre problème, avec Microsoft, qui gère
des services comme outlook.com
et
hotmail.com
. Tous les services Microsoft
rejetaient mon courrier par intermittence :
<DESTINATAIRE@outlook.com>: host outlook-com.olc.protection.outlook.com[52.101.73.31] said: 550 5.7.1 Unfortunately, messages from [92.243.4.211] weren't sent. Please contact your Internet service provider since part of their network is on our block list (S3150). You can also refer your provider to http://mail.live.com/mail/troubleshooting.aspx#errors. [AMS0EPF000001AE.eurprd05.prod.outlook.com 2023-10-07T10:03:08.034Z 08DBBB93AD81129F] (in reply to MAIL FROM command)
Résoudre le problème est très chronophage. Il faut essayer un des
innombrables moyens de contact avec Microsoft comme
, https://sender.office.com/
(le programme SNDS
- Smart Network Data
Services - ne fait qu'afficher, il ne modifie pas les listes
de blocage) ou https://sendersupport.olc.protection.outlook.com/snds/
(le troisième a
marché, pour moi, j'ai pu parler à un humain), ce qui nécessite parfois un compte
Microsoft (et une adresse Gmail pour pouvoir leur écrire…). (Rappelez-vous que les clients de Microsoft n'osent
jamais rien demander à Microsoft. Ce sont des gens comme moi, qui ne
sont pas clients de cette entreprise, qui doivent la contacter, ce
qui est évidemment moins efficace.) Bref, après des mois de
discussions kafkaïennes (Microsoft prétendant
même que mon adresse IP n'était pas bloquée alors que leur propre
message d'erreur, cité plus haut, dit explicitement le contraire),
Microsoft a fini par corriger le problème (« Thank you for
contacting the Outlook.com Deliverability Support Team. We have
implemented mitigation for your IP (92.243.4.211) and this process
may take 24 - 48 hours to replicate completely throughout our
system. »). Je peux désormais envoyer du courrier à
https://olcsupport.office.com/
outlook.com
et hotmail.com
(pour l'instant, en tout cas). Mais on se doute bien que toustes les
administrateurices de serveurs de messagerie n'ont pas envie de
dépenser autant de temps et d'effort avec cette entreprise.
Concrètement, ma plateforme actuelle est composée d'un serveur de messagerie, qui est un VPS sous Debian. (Vous pouvez trouver son nom, tout est forcément public, pour que ça fonctionne, ce qui peut avoir des conséquences en terme de vie privée.) Ce serveur est situé chez Gandi. J'aurais pu l'héberger à la maison (mon FAI bloque le port 25 - celui de SMTP - par défaut mais permet de l'ouvrir gratuitement simplement en un clic sur l'interface client) mais je n'avais pas envie de gérer l'alimentation électrique permanente, certains opérateurs qui refusent le courrier provenant d'adresses IP « résidentielles », etc. Sur cette machine tournent Postfix et les autres logiciels utiles (comme OpenDKIM).
Bien sûr, j'utilise SPF (mon enregistrement), DKIM (la clé actuelle) et même DMARC (avec une politique laxiste car DMARC casse les listes de diffusion, ce que je trouve inacceptable). Et j'ai un enregistrement inverse (de l'adresse IP vers le nom) dans le DNS. J'ai testé toutes ces configurations avec divers auto-répondeurs. Et, pour me protéger moi-même contre le spam, je valide les assertions SPF, DKIM et DMARC, ce qui ajoute au courrier des informations (RFC 8601) comme :
Authentication-Results: mail.bortzmeyer.org; dkim=pass (2048-bit key; unprotected) header.d=mailchimpapp.net header.i=rayna=rs-strategy.consulting@mailchimpapp.net header.a=rsa-sha256 header.s=k2 header.b=Gr+4sGal; dkim-atps=neutral Authentication-Results: mail.bortzmeyer.org; spf=pass (sender SPF authorized) smtp.mailfrom=mail198.atl221.rsgsv.net (client-ip=198.2.139.198; helo=mail198.atl221.rsgsv.net; envelope-from=bounce-mc.us5_165546418.9420482-fccde11058@mail198.atl221.rsgsv.net)
Comme SPF et DKIM ne font que de l'authentification (et qu'un spammeur peut lui aussi s'authentifier), j'ajoute du greylisting (RFC 6647), avec Greyfix, SpamAssassin (comme tout le monde…) et surtout bogofilter, qui est particulièrement efficace, les faux positifs sont en nombre infime.
Quelques lectures :
En conclusion, si vous m'avez écrit et que vous n'avez pas eu de réponse, c'est peut-être que votre hébergeur de courrier fait n'importe quoi et bloque mes messages. Dirigez vos reproches vers eux, pas vers moi.
Première rédaction de cet article le 5 janvier 2024
Le 3 janvier 2024, une partie du trafic IP à destination de la filiale espagnole d'Orange n'a pas été transmis, en raison d'un problème BGP, le système dont dépend tout l'Internet. Une nouveauté, par rapport aux nombreux autres cas BGP du passé, est qu'il semble que le problème vienne du piratage d'un compte utilisé par Orange. Quelles leçons tirer de cette apparente nouveauté ?
D'abord, les faits. Le 3 janvier 2024, vers 14:30 UTC, le trafic avec l'AS 12479, celui d'Orange Espagne chute brutalement. Cela se voit par exemple sur Radar, le tableau de bord de Cloudflare, recopié ici :
Pourquoi ? Le problème est lié à BGP, ce qui est logique puisque ce protocole de routage est la vraie infrastructure de l'Internet. C'est BGP qui permet à tous les routeurs de savoir par où envoyer les paquets IP. On voit l'augmentation importante du trafic BGP de cet AS sur RIPE stat :
Mais il ne s'agit pas d'un détournement par le biais d'une
annonce BGP mensongère comme on l'a vu de nombreuses fois. Ici, le
problème était plus subtil. Si on regarde l'archive des annonces BGP
à RouteViews, on ne trouve
pas une telle annonce « pirate ». Prenons le fichier d'annonces BGP
https://archive.routeviews.org/route-views3/bgpdata/2024.01/UPDATES/updates.20240103.1400.bz2
(attention, il est gros) et convertissons les données (qui étaient
au format MRT du RFC 6396), avec l'outil bgpdump : on trouve
des retraits massifs d'annonces des préfixes d'Orange Espagne
comme :
TIME: 01/03/24 14:13:17.792524 TYPE: BGP4MP_ET/MESSAGE/Update FROM: 208.94.118.10 AS40630 TO: 128.223.51.108 AS6447 WITHDRAW 85.50.0.0/22 85.51.12.0/22 85.53.56.0/22 85.53.100.0/22 85.54.36.0/22 ...
Mais pas d'annonce usurpatrice. Le problème est très différent et semble venir d'un détournement d'un mécanisme de sécurité de BGP.
En effet, BGP est, par défaut, très vulnérable. N'importe quel
routeur de la DFZ peut annoncer une route vers n'importe quel
préfixe et ainsi capter du trafic (un grand classique, on l'a
dit). Rassurez-vous, il existe plusieurs mécanismes de sécurité pour
limiter ce risque. Mais comme rien n'est parfait en ce bas monde,
ces mécanismes peuvent à leur tour créer des problèmes. En
l'occurrence, le problème semble avoir été avec la
RPKI. Ce système, normalisé dans le RFC 6480, permet de signer
les ressources Internet, notamment les préfixes d'adresses IP (comme
85.50.0.0/22
et les autres cités plus haut). Un
des objets que permet la RPKI est le ROA (Route Origination
Authorization, RFC 6482), qui
déclare quel(s) AS
peuvent être à l'origine d'une annonce d'un préfixe. Il y a
normalement un ROA pour les préfixes d'Orange Espagne (il se voit,
ainsi que sa validité, sur le service de
Hurricane Electric, ou bien sur
RIPE stat). Mais, pendant le problème, ce ROA avait disparu,
remplacé par un autre qui donnait comme origine l'AS
49581 (qui, j'insiste sur ce point, est apparemment
totalement innocent dans cette affaire et semble avoir été choisi au
hasard). Les annonces BGP d'Orange Espagne étaient donc refusés par
les routeurs validants, ce qui explique les retraits de route comme
celui montré plus haut, d'où l'agitation BGP, et la chute du trafic,
bien des routeurs ne sachant plus comment joindre les préfixes
d'Orange Espagne.
S'agissait-il d'une erreur d'Orange ? Apparemment pas. Une personne identifiée comme « Ms_Snow_OwO » s'est vantée sur Twitter d'avoir provoqué le problème. Le message a depuis disparu mais voici une copie d'écran :
Notez aussi les copies d'écran envoyées par « Ms_Snow_OwO », montrant bien l'interface du RIPE-NCC avec les ressources (notamment les préfixes IP) d'Orange Espagne :
En Europe, la très grande majorité des opérateurs qui créent des ROA ne le font pas sur une machine à eux (ce que la RPKI permet, puisqu'elle est décentralisée) mais sous-traitent cette opération au RIR, le RIPE-NCC. L'opérateur se connecte à une interface Web, le LIR Portal, s'authentifie et indique les préfixes qu'il veut voir signés. On voit donc qu'un maillon nécessaire à la sécurité est l'authentification sur le portail qui sert aux opérateurs Internet. Le RIPE-NCC permet une authentification à deux facteurs, via le protocole TOTP (normalisé dans le RFC 6238), mais ce n'est pas obligatoire et, selon « Ms_Snow_OwO », le mot de passe était bien trop simple. L'attaquant a pu alors s'authentifier auprès du RIPE-NCC et changer le ROA, cassant ainsi le routage.
C'est un problème courant en sécurité : toute technique qui vise à empêcher l'accès aux méchants peut être détournée pour faire un déni de service. Ainsi, par exemple, si vous bloquez un compte au bout de N tentatives d'accès infructueuses (une très mauvaise idée, mais qu'on voit parfois), il est trivial pour un attaquant de bloquer le compte, juste en tapant n'importe quel mot de passe. Ici, on peut s'indigner de ce qu'une technique anti-usurpation mène à un déni de service mais c'est un problème bien plus vaste que la RPKI.
Comme des informations ont montré que le mot de passe d'Orange Espagne était bien trop faible (juste « ripeadmin »), beaucoup de gens ont ricané, parfois bêtement, sur cette faiblesse. En fait, comme l'attaquant a apparemment utilisé un logiciel malveillant installé sur l'ordinateur d'un employé d'Orange Espagne, il aurait pu avoir le meilleur mot de passe du monde (« 45cf*b2b44cfA7🦕f64ccç302617F! »), cela n'aurait rien changé. Plutôt que de se focaliser sur ce mot de passe, effectivement trop faible, il vaudrait mieux insister sur l'importance d'activer l'authentification à deux facteurs, comme le recommande le RIPE.
Quelques lectures sur cette attaque, presque toutes en anglais car je n'ai rien trouvé en français :
Première rédaction de cet article le 1 décembre 2023
Si vous êtes programmeuse ou programmeur, vous avez peut-être des problèmes de portabilité de vos programmes, et vous souhaitez les tester sur d'autres plate-formes que celles où vous avez accès habituellement. C'est le but des fermes de compilation et cet article va présenter la Cfarm, sur laquelle je viens d'avoir un compte.
Donc, le principe d'une ferme de compilation : beaucoup de machines, de types et de systèmes d'exploitation différents, auxquelles vous pouvez accéder via SSH et tester vos programmes. C'est surtout utile pour les langages de bas niveau comme C, il y a sans doute moins de problèmes de portabilité en Python ou Haskell…
La Cfarm est réservée aux programmes libres. Pour avoir un compte, vous devez remplir un formulaire où vous indiquez notamment les projets de logiciel libre que vous maintenez. Votre compte est ensuite approuvé (dans mon cas, cela avait pris moins de 24 heures) et vous pouvez alors vous connecter sur le site Web, et indiquer des clés SSH qui seront automatiquement copiées sur toutes les machines, vous permettant de vous connecter (mais vous ne serez évidemment pas root). La liste des machines est longue ! Et elle contient des types devenus rares, par exemple des processeurs PowerPC, qui sont utiles car ce sont dans les rares à être gros-boutiens. J'aime bien aussi la machine ARM faisant tourner FreeBSD. Et, oui, il y a une RISC-V, si vous ne voulez pas en acheter une.
Pour vous simplifier la vie, si vous travaillez sur plusieurs de
ces machines, OpenSSH permet des
configurations globales, voici mon
~/.ssh/config
:
Host *.cfarm.net User bortzmeyer IdentityFile ~/.ssh/id_cfarm
Notez que j'utilise une clé ed25519 mais la documentation note bien que certains vieux systèmes ne connaissent pas cet algorithme, et elle suggère d'utiliser RSA, si on veut passer partout (on peut avoir plusieurs clés dans sa configuration Cfarm).
Cette configuration est également utile si la machine utilise un port inhabituel, ce que font certaines.
Auteur(s) du livre : Laure Daussy
Éditeur : Les échappés
978-2-35766-198-1
Publié en 2023
Première rédaction de cet article le 26 novembre 2023
Ce livre parle d'un féminicide, l'assassinat de Shaïna Hansye en 2019. Et regarde de près l'environnement où ce crime a eu lieu. Ce n'était pas un fait isolé, mais le produit de plusieurs phénomènes, dont le sexisme et les injonctions à la « pureté » contre les filles et femmes.
Le meurtre a eu lieu à Creil et l'assassin était le « petit ami » de la victime. La journaliste Laure Daussy est allé interroger les parents de Shaïna, ses amies, les enseignants, les associations pour comprendre ce qui s'est passé. Creil cumule les problèmes : services publics supprimés ou défaillants, chômage massif, montée de l'islamisme, etc. Pour ne citer qu'un exemple, la maternité, où les jeunes filles pouvaient avoir des conseils sur la contraception et l'IVG a fermé, les habitantes sont censées aller à Senlis, ville avec laquelle il n'y a pas de liaison par transports en commun. Les jeunes filles sans permis de conduire sont donc privées de toute information, sauf si leurs parents acceptent de les emmener (ce qui n'est pas toujours facile à demander !). Les « territoires perdus de la République », dont les médias aiment bien parler, c'est aussi ça : une ville abandonnée des pouvoirs publics. Un autre exemple est donné par l'incroyable passivité de la police lorsque Shaïna, deux ans avant le meurtre, était allé porter plainte pour un viol. Beaucoup de femmes n'osent pas porter plainte, craignant (à juste titre) qu'elles se retrouvent accusées ou stigmatisées ou bien que cela ne serve à rien. Ici, l'enquête de la police et l'instruction judiciaire se sont déroulées avec lenteur, le jugement n'ayant eu lieu qu'après le meurtre de la victime.
Mais l'un des principaux problèmes n'est pas directement lié à cet abandon. C'est la question de la réputation, qui donne son titre à l'enquête. Des hommes ont décidé de contrôler la vie des femmes et notamment par le biais du contrôle de leur vie affective et sexuelle. Une jeune fille qui vit un peu librement peut vite se retrouver marquée par l'étiquette « fille facile ». Souvent, il n'est même pas nécessaire de vouloir vivre sa vie, toute fille peut se retrouver ainsi étiquetée, par exemple parce qu'un homme a voulu se « venger » d'elle. Si personne, parlant à la journaliste, n'a osé défendre ouvertemement le meurtre de Shaïna, en revanche plusieurs de ses interlocuteurs ont relativisé ce meurtre, le justifiant par le statut de « fille facile » de la victime.
Le terme revient souvent lorsque les hommes (et parfois aussi les femmes) parlent à l'auteure. Le poids des préjugés, celui, grandissant, de la religion, le sexisme se combinent pour enfermer les femmes de Creil. Toutes portent en permanence le poids de cette injonction à la réputation.
Première rédaction de cet article le 24 novembre 2023
Un peu de programmation aujourd'hui. Supposons qu'on reçoive des messages qui ont été modifiés en cours de route et qu'on veut remettre dans leur état initial. Comment faire cela en Python ?
Comme exemple, on va supposer que les messages contenant le mot « chiffrer » ont été modifiés pour mettre « crypter » et qu'on veut remettre le terme correct. On va écrire un programme qui reçoit le message sur son entrée standard et met la version corrigée sur la sortie standard. D'abord, la mauvaise méthode, qui ne tient pas compte de la complexité du courrier électronique :
import re import sys botched_line = re.compile("^(.*?)crypter(.*?)$") for line in sys.stdin: match = botched_line.search(line[:-1]) if match: # re.sub() serait peut-être meilleur ? newcontent = match.group(1) + "chiffrer" + match.group(2) + "\n" else: newcontent = line print(newcontent, end="")
On lit l'entrée standard, on se sert d'une expression rationnelle (avec le module re) pour trouver les lignes pertinentes, et on les modifie (au passage, le point d'interrogation à la fin des groupes entre parenthèses est pour rendre l'expression non gourmande). Cette méthode n'est pas bonne car elle oublie :
Il faut donc faire mieux.
Il va falloir passer au module email. Il fournit tout ce qu'il faut pour analyser proprement un message, même complexe :
import sys import re import email import email.message import email.policy import email.contentmanager botched_line = re.compile("^(.*?)crypter(.*?)$") msg = email.message_from_file(sys.stdin, _class=email.message.EmailMessage, policy=email.policy.default) for part in msg.walk(): if part.get_content_type() == "text/plain": newcontent = "" for line in part.get_content().splitlines(): match = botched_line.search(line) if match: # re.sub() serait peut-être meilleur ? newcontent += match.group(1) + "chiffrer" + match.group(2) + "\n" else: newcontent += line + "\n" email.contentmanager.raw_data_manager.set_content(part, newcontent) print(msg.as_string(unixfrom=True))
Ce code mérite quelques explications :
email.message_from_file
lit un fichier
(ici, l'entrée standard) et rend un objet Python de type
message. Attention, par défaut, c'est un ancien type, et les
opérations suivantes donneront des messages d'erreur
incompréhensibles (comme « AttributeError: 'Compat32'
object has no attribute 'content_manager' » ou
« AttributeError: 'Message' object has no attribute
'get_content'. Did you mean: 'get_content_type'? »). Les
paramètres _class
et
policy
sont là pour produire des messages
suivant les types Python modernes.walk()
va parcourir les différentes
parties MIME du message, récursivement.get_content_type()
renvoie le
type MIME de la partie et nous ne nous
intéressons qu'aux textes
bruts, les autres parties sont laissées telles
quelles.get_content()
donne accès aux données
(des lignes de texte) que splitlines()
va
découper.set_content()
remplace l'ancien contenu
par le nouveau.as_string
transforme l'objet
Python en texte. Notre message a été transformé.Ce code peut s'utiliser, par exemple, depuis procmail, avec cette configuration :
:0fw | $HOME/bin/repair-email.py
Évidemment, il peut être prudent de sauvegarder le message avant cette manipulation, au cas où. En procmail :
:0c: Mail/unmodified
Vous voulez tester (sage précaution) ? Voici un exemple de message, fait à partir d'un spam reçu :
From spammer@example Fri Nov 24 15:08:06 2023 To: <stephane@bortzmeyer.org> MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_Part_9748132_1635010878.1700827045631" Date: Fri, 24 Nov 2023 13:42:17 +0100 (CET) Subject: 🍷 Catalogues vins From: Ornella FEDI <ornella.fedi@vinodiff.com> Content-Length: 65540 Lines: 2041 ------=_Part_9748132_1635010878.1700827045631 Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable Cet E-mail dit "crypter". Pour pouvoir afficher cet E-mail, le clie= nt de messagerie du destinataire doit supporter ce format. ------=_Part_9748132_1635010878.1700827045631 Content-Type: text/html; charset="UTF-8" Content-Transfer-Encoding: quoted-printable <?xml version=3D"1.0" ?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns=3D"http://www.w3.org/1999/xhtml"> <head> <title>E-Mail</title> <style type=3D"text/css" media=3D"screen"> </style> </head> <body>Deleted to save room</body></html> ------=_Part_9748132_1635010878.1700827045631--
Mettez-le dans un fichier, mettons spam.email
et passez le au programme Python :
% cat /tmp/spam.email | ./repair.py From spammer@example Fri Nov 24 15:08:06 2023 To: <stephane@bortzmeyer.org> MIME-Version: 1.0 Content-Type: multipart/alternative; boundary="----=_Part_9748132_1635010878.1700827045631" Date: Fri, 24 Nov 2023 13:42:17 +0100 (CET) ... ------=_Part_9748132_1635010878.1700827045631 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Cet E-mail dit "chiffrer". Pour pouvoir afficher cet E-mail, le client de mes= sagerie du destinataire doit supporter ce format. ------=_Part_9748132_1635010878.1700827045631 ...
Si vous voulez améliorer ce programme, vous pouvez gérer les cas :
+
par
quelque chose de plus rapide (je cite Bertrand Petit : concaténer des chaines en Python est
couteux. Chaque application de l'opérateur + crée une nouvelle
chaîne qui n'aura qu'une existence brève, et cela cause
aussi, à chaque fois, la copie de l'intéralité du contenu de la
chaine en croissance. À la place, il vaudrait mieux stocker chaque
bout de chaine dans une liste, pour finir par tout assembler en
une fois, par exemple avec join
).Première rédaction de cet article le 22 novembre 2023
Les 18 et 19 novembre 2023, c'était Capitole du Libre à Toulouse. Plein d'exposés intéressants et de rencontres passionnantes autour du thème du logiciel libre.
C'est gros, le Capitole du Libre : 1 200 personnes sont venues, il y avait 118 orateurices, 94 conférences, 18 ateliers et 31 stands. Comme il y a eu davantage de propositions que de créneaux, certaines ont été refusées. 50 kg de pop-corn ont été légués par le DevFest dont 20 ont été consommés. Mais il y avait aussi des pains au chocolat :
Personnellement, j'ai fait un exposé sur la
censure sur
l'Internet. Les supports au format PDF sont
(et leur source
en capitole-libre-2023-censure.pdf
). La vidéo a été
faite mais n'est pas encore publiée. (Amusant calendrier, cet exposé
sur la censure a été fait deux jours avant la publication du RFC 9505.)capitole-libre-2023-censure.tex
Ironie de la situation, le réseau de l'ENSEEIHT, l'école qui héberge gratuitement le Capitole du Libre, met en œuvre une des techniques de censure présentées. Si on tente de se connecter à OnlyFans, on récupére un RST (ReSeT) TCP, probablement suite à l'examen du SNI dans l'ouverture de la session TLS. Vu par tcpdump, voici ce que ça donne (le dernier paquet, le RST, n'est pas légitime) :
13:19:35.603931 IP 172.22.223.35.40874 > 54.211.0.120.443: Flags [S], seq 2413541568, win 64240, options [mss 1460,sackOK,TS val 46581492 ecr 0,nop,wscale 7], length 0 13:19:35.744066 IP 54.211.0.120.443 > 172.22.223.35.40874: Flags [S.], seq 3076610656, ack 2413541569, win 26847, options [mss 1460,sackOK,TS val 453378101 ecr 46581492,nop,wscale 8], length 0 13:19:35.744173 IP 172.22.223.35.40874 > 54.211.0.120.443: Flags [.], ack 1, win 502, options [nop,nop,TS val 46581633 ecr 453378101], length 0 13:19:35.830075 IP 172.22.223.35.40874 > 54.211.0.120.443: Flags [P.], seq 1:518, ack 1, win 502, options [nop,nop,TS val 46581719 ecr 453378101], length 517 13:19:35.833848 IP 54.211.0.120.443 > 172.22.223.35.40874: Flags [R.], seq 1, ack 518, win 502, length 0
Et ne dites pas d'utiliser Tor, les nœuds d'entrée publics sont bloqués.
Maintenant, les bons exposés (dans l'ordre arbitraire que j'ai décidé tout seul avec mes préférés en premier). Vincent Privat a présenté un système pour récupérer automatiquement les photos que publie la NASA (avec leurs métadonnées) et les mettre sur Wikimedia Commons. C'est légal puisque la NASA, comme toutes les agences fédérales étatsuniennes, met tout ce qu'elle publie dans le domaine public. Mais c'est techniquement difficile. Déjà, s'il y a parfois une API pour lister les images à récupérer, parfois, il faut plutôt scraper l'HTML. Et parfois il faut deviner l'URL en testant plusieurs variantes. D'autre part, il faut éliminer les doublons (chacun peut écrire dans Wikimedia Commons et ces images sont souvent importées). Son outil récupère toutes les métadonnées, les vérifie et convertit vers ce qu'attend Wikimedia Commons. Comme toujours avec l'open data, il y a des bavures amusantes, par exemple la date de prise de vue indiquée est parfois dans le futur, il y a du mojibake dans les textes, des licences incohérentes (entre le texte et les EXIF), des « descriptions » qui sont en fait de la publicité, etc (tous des problèmes classiques de l'open data). D'autre part, beaucoup d'outils de Wikimedia sont conçus pour le texte, car la cible est Wikipédia. Utiliser les outils et machines de Wikimedia pour traiter des images de beaucoup de giga-octets (par exemple pour le dédoublonnage) explosait les quotas. Enfin, pour dédoublonner, le programme calcule un perceptual hashing, pas un simple et traditionnel condensat.
À noter que les autres agences spatiales ne mettent rien sous licence libre donc la vision de l'espace est dominée par celle des États-Unis, les seuls à avoir compris la force du soft power.
Julian Vanden Broeck a fait un trés bon bilan de son changement
du système de messagerie instantanée interne dans son entreprise
(passage à Matrix). Comment ça marche en
vrai ? Quel accompagnement pour les utilisateurices ? Quelle
acceptation ? Entre utilisateurices qui râlaient contre l'ancien
système, celleux qui râlaient contre le nouveau, celleux qui
disaient que Microsoft Teams, c'était mieux,
et celleux qui critiquaient la couleur des boutons, l'acceptation
n'était pas évidente. La technique,
c'est facile, ce sont les humains qui sont compliqué·es. « On a
désactivé le chiffrement systématique car c'était trop frustrant
pour les utilisateurs, notamment en cas de perte de clés. » Je
confirme que c'est aussi mon expérience avec Matrix, loin des
promesses d'un système parfait. Et encore, l'entreprise de l'orateur
n'utilise que le client Element, les problèmes sont pires quand on a
plusieurs clients (de ce point de vue, Matrix ne marque aucun
progrès par rapport à XMPP, où l'incompatibilité des
clients a toujours été une plaie). Au passage, si vous voulez
essayer, je suis joignable à
@bortzmeyer:matrix.underworld.fr
.
On s'est bien amusés avec l'exposé « Modèles d'organisation ouverts dans les entreprises du logiciel libre » car les deux orateurs ne se sont pas présentés, sans explication. Thomas Petazzoni a alors lancé l'idée que, puisque le sujet était les organisations ouvertes, non hiérarchiques, etc, on n'avait pas besoin d'orateurs et on pouvait débattre entre nous. Ça a très bien pris et il y a eu une discussion intéressante, par exemple avec les gens d'une association, l'Échappée Belle, qui est en fait une coopérative de freelances, où chaque salarié décide de son salaire. Évidemment, ils insistaient sur le fait que ça ne peut sans doute marcher que dans les petites organisations (ils sont six), où les gens se connaissent et ont des valeurs communes.
Et Pierre-Yves Lapersonne a posé la question du choix des bibliothèques logicielles quand on développe un logiciel libre. Le ou la programmeureuse ne peut pas tout faire, il faut donc dépendre de bibliothèques existantes. Vous, je ne sais pas mais moi, quand je commence un projet, j'ai toujours du mal à identifier les bonnes bibliothèques dont mon projet va dépendre. Vous utilisez quoi, comme critères ? Quand il y a le choix, c'est compliqué. Comment choisir, et, par exemple, détecter celles qui ne sont plus maintenues (donc où les bogues ne seront plus corrigées), celles gérées par une boite qui va subitement changer la licence (il y a eu deux-trois cas spectaculaires ces dernières années, comme MongoDB), etc. Parmi les critères possibles (mais aucun n'est parfait et l'auteur recommande de se méfier des critères quantitatifs simples) :
git
rebase
…),On a aussi discuté d'UI et d'UX au Capitole du Libre. Nojhan a parlé de LiquidPrompt, un des nombreux mécanismes permettant d'avoir des invites très riches pour la ligne de commande du shell Unix. L'orateur a commencé par : « vous utilisez toustes le shell ? », ce qui évidemment fait rire au Capitole du Libre. Le but du projet est d'améliorer l'invite de la ligne de commande en indiquant davantage d'informations. Le problème est évidemment de ne pas surcharger l'utilisateurice. LiquidPrompt, comme ses concurrents, permet d'avoir plusieurs thèmes, et le thème par défaut est dense et plein d'informations. (Y compris la charge de la batterie, ce que je trouve anxiogène.) Pour les démonstrations, c'est spectaculaire. Une autre démonstration montrait un thème où l'invite occupe tout le terminal tellement il y a d'informations.
Un gros avantage de ces systèmes d'invites riches est l'indication de l'état du dépôt Git (ou bien parfois d'autres VCS) où on se trouve. On peut indiquer la branche, bien sûr (combien de fois je me suis planté, me trompant sur la branche où j'étais) mais aussi des suggestions sur les commandes git qui pourraient être pertinentes.
Bon, il y a tellement de systèmes d'invites shell améliorées que c'est un travail en soi d'en choisir une ! L'auteur a fait un article de comparaison.
Maïtané Lenoir a parlé de « modération » (terme que je trouve contestable) sur les réseaux sociaux, notamment les « réseaux sociaux capitalistes » (ce qui inclut TikTok, même si ça déplait au PC chinois). « Je vais vous raconter des trucs traumatiques. » Déjà, elle a noté qu'il fallait bien différencier la politique officiellement affichée, et les moyens qui sont effectivement mis en œuvre pour l'appliquer. (Ce principe doit pouvoir s'appliquer à la politique en général.) Elle a insisté sur l'importance d'une riposte graduée. Si un logiciel ne permet que des réactions simplistes et binaires (supprimer le compte, par exemple), le contrôle sera difficile car on n'aura le choix qu'entre deux mauvaises décisions. Parmi les difficultés (que les réseaux sociaux capitalistes mettent sous le tapis) du contrôle, la langue. Il ne suffit pas de lire la langue utilisée, il faut aussi connaitre la culture, pour comprendre allusions, jeux de mots, choix de vocabulaire. Un Français va avoir du mal à évaluer du contenu écrit en français par un Ivoirien ou un Québecois.
Et, bien sûr, le contrôle prend du temps et des efforts et les réseaux sociaux capitalistes, quoi qu'ils prétendent, ne mettent pas assez de moyens. L'IA n'est pas une solution. (Notez que Pharos a la même analyse.)
L'auteure travaille dans l'équipe de modération de Framasoft, par exemple pour l'instance fédivers Framapiaf. Inutile de dire que ce n'est pas facile, cette équipe a dû faire face à des campagnes s'attaquant à Framasoft et à sa modération (prétendant par exemple qu'il n'y en avait pas). La publication de la charte de modération n'a rien arrangé, les attaques des ultras du contrôle contre Framasoft ont redoublé, tournant au harcèlement. Heureusement qu'en interne, le collectif était soudé.
L'oratrice a beaucoup insisté sur les difficultés pratiques, quotidiennes de la modération : par exemple, beaucoup de signalements ne donnent aucun détail. C'est quoi, le problème, exactement ? Les modérateurs doivent essayer de deviner. Signaleurs, détaillez vos messages ! (Sinon, elle n'en a pas parlé, mais je trouve qu'il y a aussi des signalements malveillants, pour faire taire des ennemis politiques. Les islamistes font souvent cela, par exemple.) Conseil : ne pas oublier le temps et l'énergie consommée. Certes, la modération de Framapiaf n'est pas parfaite (aucune ne l'est) mais un des principes est : « ne pas passer un temps infini sur chaque cas » (surtout que les modérateurs sont bénévoles). Or, les signaleurs sont souvent très exigeants « on veut une action safe tout de suite !!! »
La conclusion : Modération = Pouvoir et Humain = Faillible.
On a eu évidemment une table ronde sur l'IA, où on a surtout parlé de régulation. Celle-ci évidemment ne vise pas forcément qu'à protéger les gens, elle peut avoir des motivations protectionnistes. Florence Sèdes a noté que le récent projet Kyutai de Niel et Saadé ne mette en jeu que des sommes « dérisoires ». Toujours dans l'actualité chargée de l'IA, j'ai demandé à ChatGPT les raisons du licenciement spectaculaire de la direction d'OpenAI, la veille du Capitole du Libre, licenciement qui fait parler toute la Silicon Valley mais il m'a répondu qu'il ne savait pas, qu'à sa connaissance, Altman était toujours CEO.
L'autre table ronde portait sur la sobriété, avec notamment Agnès Crepet, qui bosse chez Fairphone. La table ronde a été un peu plus animée car il y avait aussi le député Modem Philippe Latombe, spécialiste des questions numériques, mais qui a voté la loi JO, avec l'extension de la vidéo-surveillance. Indépendamment de la question des droits humains, la vidéo-surveillance est un gouffre énergétique, il s'est fait interpeller là-dessus, a agité le chiffon rouge en disant qu'il ne fallait pas dire vidéo-surveillance mais vidéo-protection (comme on dit dans les comptes rendus de débats « mouvements divers ; protestations »), bref, a contribué à rappeler que la sobriété numérique n'était pas consensuelle. Les JO, qui sont le prétexte pour généraliser cette surveillance, sont une grande source de gaspillage, et Bookynette, qui animait la table ronde, a appelé à boycotter cette fête du fric et du dopage.
Sinon, en matière d'empreinte environnementale du numérique, ne pensez pas qu'à la consommation électrique, note Agnès Crepet. Il y a aussi l'extraction des métaux, dans des conditions désastreuses. Elle a aussi parlé de la réparabilité, sa tante Ginette étant capable de démonter un ordiphone. Ginette est plus habile de ses mains que moi, elle arrive à réparer son Fairphone, alors que j'ai bousillé un Fairphone 2 en essayant d'intervenir à l'intérieur.
Autre participant à la table ronde, Gaël Duval (/e/). Il a parlé de l'obsolescence logicielle. Pour limiter la surconsommation de matériel, il faut aussi maintenir du logiciel qui continue à tourner sur les « vieux » (10 ans) équipements. Par exemple, les vieux trucs 32 bits ont du mal avec des applications qui sont en 64 bits maintenant. Philippe Latombe note que l'État n'est pas forcément cohérent, par exemple en prônant la réutilisation tout en taxant (avec le racket « copie privée ») les téléphones reconditionnés. Agnès Crepet a appelé au « techno-discernement » ; la technique n'est pas forcément utile. Parfois, la solution est de ne pas numériser.
Guillaume a parlé de l'intégration des contributions externes dans le compilateur Rust. Il a expliqué comment les auteurs de Rust géraient ces contributions, entre le désir de favoriser les contributions extérieures (un gros point fort du logiciel libre) et la nécessité de contrôler (pour éviter bogues, voire, pires, attaques « supply chain »). L'équilibre est délicat. Une bonne partie du processus d'examen des contributions est automatisée, via la CI. Si le changement est léger et que la CI passe, ça va plus vite. Sinon, jetez un coup d'œil à la file d'attente de Rust. Évidemment, il faut que tous les tests passent et, rien que pour les messages d'erreur du compilateur, il y a plusieurs dizaines de milliers de tests. Les machines tournent longtemps ! Si vous voulez contribuer au compilateur Rust, certains tickets sont marqués « pour débutants » et sont a priori plus faciles. Vous pouvez voir pas mal d'articles sur Rust au blog de l'orateur.
Adrien Destugues a fait un exposé très geek, la rétro-ingéniérie d'un jeu vidéo où il jouait quand il était petit (le logiciel ludo-éducatif « Lectures Enjeu »), jeu commercial abandonné et qu'il voulait faire revivre. Aucune perspective utilitaire ou financière, c"était juste de l'art pour l'art. Amusant, les chaines de caractères du jeu sont paramétrées avec un @ qui est remplacé par une marque du féminin si la joueuse a indiqué qu'elle était du genre féminin. "arrivé@" dans le fichier est affiché "arrivé" ou "arrivée" selon le cas.
Un sujet financier ? « La blockchain pour le financement du logiciel libre » par Guillaume Poullain, par exemple. Gitcoin est une organisation de financement du logiciel libre (50 M$ distribués en 2023). Ça utilise le protocole Allo (je ne connais pas). Les gens qui financent ont un droit de vote. L'allocation est quadratique, favorisant le nombre de votes davantage que le montant (pour éviter qu'un millionaire n'ait autant de poids que mille smicards). 3 715 projets ont été financés, 3,8 M$ de donations (je n'ai pas vérifié tous les chiffres).
Un des problèmes est évidemment ce sont les projets les plus connus (souvent les plus riches) qui ont le plus de votes. Et, évidemment, certains projets reçoivent peu de votes et donc de fric. Il y a aussi le risque d'attaque Sybil, favorisé par l'allocation quadratique. (Solution : une vérification d'unicité avec un système nommé Passport.)
À noter qu'on ne peut pas retirer l'argent, le flux est dans un seul sens (ce qui évite les bogues style The DAO qui sont la plaie des fonds d'investissement tournant sur la chaine de blocs). Bref, mon opinion :ça me semble une bonne utilisation des possibilités de la chaine de blocs.
Merci à l'école qui prête les locaux gratuitement, aux sponsors. Et surtout aux bénévoles :
Auteur(s) du livre : David Snug
Éditeur : Nada
9-791092-457513
Publié en 2022
Première rédaction de cet article le 20 novembre 2023
J'avoue, j'ai acheté cette BD pour le titre, référence à un ancien slogan anarchiste. Il y a d'autres jeux de mots dans ce livre. Mais le point important est que c'est une critique vigoureuse du Web commercial et de ses conséquences sur la société.
L'inconvénient des librairies comme Quilombo est qu'une fois qu'on y va pour quelque chose de précis, on se laisse prendre et on achète des livres pas prévus. En l'occurrence, la librairie a pas mal de livres luddites, assez variés. Cette BD imagine un auteur un peu âgé, fan de toutes les possibilités du Web commercial, et qui rencontre son moi du passé qui a voyagé dans le temps pour voir comment était le futur. Le David Snug du passé découvre donc Amazon, Uber Eats, Spotify, etc.
J'aime le style des dessins, et la vision de la ville moderne, où tout le monde est accroché à son ordiphone sonne juste. De même, l'auteur dénonce avec précision les conséquences concrètes du Web commercial, comme l'exploitation des travailleurs sans-papier pour la livraison des repas. C'est souvent très drôle, et rempli de jeux de mots mêlant termes du monde numérique et vieux slogans anarchistes. (Il y en a même un sur les noms de domaine.) C'est parfois, comme souvent avec les luddites, un peu trop nostalgique (« c'était mieux avant ») mais, bon, le fait que le passé connaissait déjà l'exploitation de l'homme par l'homme n'excuse pas le présent.
Bref, c'est un livre que je peux conseiller pour des gens qui n'ont pas perçu le côté obscur du Web commercial. (Si, par contre, vous lisez tous les articles du Framablog, vous n'apprendrez sans doute rien, mais vous passerez quand même un bon moment.) Je regrette quand même que l'auteur ne semble connaitre de l'Internet que le Web et du Web que les GAFA. Ainsi, Wikipédia est expédié en une phrase, pas très correcte.
Un conseil pour finir : ne lisez pas la postface qui, elle est franchement conservatrice et défend une vision passéiste du monde, d'un courant politique qu'on peut qualifier d'« anarchiste primitiviste ». Encore pire, la bibliographie, qui va des réactionnaires comme Michel Desmurget à l'extrême-droite, avec Pièces et Main d'Œuvre. C'est là qu'on se rend compte que le courant luddite a du mal à trancher avec les défenseurs de l'ordre naturel sacré.
Auteur(s) du livre : Matthias Kirschner, Sandra
Brandstätter
Éditeur : C&F Éditions
978-2-37662-075-4
Publié en 2023
Première rédaction de cet article le 8 novembre 2023
Ce livre pour enfants a pour but de sensibiliser au rôle du logiciel dans nos sociétés. Difficile aujourd'hui d'avoir quelque activité que ce soit, personnelle ou professionnelle, sans passer par de nombreux programmes informatiques qui déterminent ce qu'on peut faire ou pas, ou, au minimum, encouragent ou découragent certaines actions. Les enfants d'aujourd'hui vont vivre dans un monde où ce sera encore plus vrai et où il est donc crucial qu'ils apprennent deux ou trois choses sur le logiciel, notamment qu'il existe du logiciel libre.
Le livre est originellement écrit en allemand, je l'ai lu dans la traduction française, publiée chez C&F Éditions. Il a été écrit à l'initiative de la FSFE.
Donc, l'histoire. Zangemann (un mélange de Jobs, Gates, Zuckerberg et Musk) est un homme d'affaires qui a réussi en fabriquant entre autres des objets connectés dont il contrôle complètement le logiciel, et il ne se prive pas d'appliquer ses règles suivant sa volonté du moment. Les utilisateurices des objets sont désarmé·es face à ces changements. Ada est une petite fille qui commence par bricoler du matériel (c'est plus facile à illustrer que la programmation) puis comprend le rôle du logiciel et devient programmeuse (de logiciels libres, bien sûr). Je ne vous raconte pas davantage, je précise juste, pour mes lecteurices programmeur·ses que ce n'est pas un cours de programmation, c'est un conte pour enfants. Le but est de sensibiliser à l'importance du logiciel, et d'expliquer que le logiciel peut être écrit par et pour le peuple, pas forcément par les Zangemann d'aujourd'hui.
Le livre est sous une licence libre. J'ai mis une illustration sur cet article car la licence est compatible avec celle de mon blog, et cela vous permet de voir le style de la dessinatrice :
Je n'ai par contre pas aimé le fait que, à part pour les glaces à la framboise, les logiciels ne soient utilisés que pour occuper l'espace public sans tenir compte des autres. Zangemann programme les planches à roulette connectées pour ne pas rouler sur le trottoir et donc respecter les piétons ? Ada écrit du logiciel qui permet aux planchistes d'occuper le trottoir et de renverser les personnes âgées et les handicapé·es. L'espace public est normalement un commun, qui devrait être géré de manière collective, et pas approprié par les valides qui maitrisent la programmation. Le film « Skater Girl » représente bien mieux cette tension entre planchistes et autres utilisateurs. Un problème analogue se pose avec les enceintes connectées où la modification logicielle va permettre de saturer l'espace sonore (un comportement très macho, alors que le livre est censé être féministe) et de casser les oreilles des autres. Remarquez, cela illustre bien le point principal du livre : qui contrôle le logiciel contrôle le monde.
Le livre parait en français le premier décembre. La version originale est déjà disponible, ainsi que la version en anglais.
Première rédaction de cet article le 3 novembre 2023
Je suis en train d'apprendre le langage de programmation Zig donc je vous fais profiter ici de ce que j'ai appris. Je ne vais pas détailler les points secondaires, comme la syntaxe, mais plutôt parler des concepts originaux de Zig, comme sa gestion d'erreurs, ses variables optionnelles ou son modèle de gestion mémoire.
Attention, si vous êtes sénateur, ne lisez pas ce texte, il utilise plusieurs techniques d'écriture inclusive. Le risque de crise cardiaque est particulièrement élevé après un déjeuner bien arrosé au restaurant du Sénat.
D'abord, le cahier des charges. Zig est un langage de bas niveau, au sens où il est conçu pour des programmes où le·a programmeur·se contrôle plein de détails, comme l'allocation de mémoire. Il est prévu pour programmer sur le métal nu (par exemple programmation dite « embarquée » ou bien pour faire un noyau de système d'exploitation). Mais on peut aussi l'utiliser sur un environement plus perfectionné, par exemple sur Unix pour écrire des serveurs Internet (le domaine de la programmation que je connais le mieux).
Zig est donc un concurrent de C mais se voulant plus sécurisé, C s'étant distingué au cours de sa longue histoire par la facilité avec laquelle le·a programmeur·se peut produire des failles de sécurité. Bien d'autres langages sont sur ce créneau, le plus connu (et le seul déjà largement utilisé en production) étant Rust. Mais il y a aussi V, Odin, Vale… (Une liste a été compilée.)
Zig a démarré en 2016 et n'est donc pas un langage si récent que cela. Mais il est toujours officiellement expérimental (l'actuelle version stable est la 0.11, j'ai travaillé avec la 0.12) et le langage continue à évoluer, ce qui rend difficile de l'utiliser pour des projets réels. En outre, sa bibliothèque standard évolue encore davantage que le langage lui-même. Il est ainsi fréquent qu'on trouve avec un moteur de recherche des articles prometteurs… mais dépassés et dont les exemples ne compilent même pas.
Bon, connaissant les lecteurices de ce blog, je pense qu'ielles sont toustes en train de se demander à quoi ressemble Hello, world en Zig. Donc :
const std = @import("std"); pub fn main() !void { const public = "Blog"; std.debug.print("Hello, {s}!\n", .{public}); }
Vous noterez qu'il faut importer explicitement la bibliothèque
standard, contrairement à la grande majorité des langages de
programmation. Une version plus longue de ce premier programme est
. En décommentant les
lignes qui commencent par deux barres obliques, vous découvrirez en
outre que :
hello.zig
Bon, je vous ai suggéré d'essayer mais, pour cela, il faudrait un compilateur. La mise en œuvre actuelle de Zig est en C++ mais une version en Zig existe et est déjà capable de se compiler elle-même. En attendant, on va utiliser le binaire fourni car je suis paresseux :
% zig version 0.12.0-dev.1245+a07f288eb % zig build-exe hello.zig % ./hello Hello, Blog!
Vous verrez un message pendant un moment disant que le code est généré par LLVM. Un des avantages de cette méthode est de rendre Zig très portable. Ainsi, les programmes tournent tous sur ma carte RISC-V.
D'autre part, la commande zig joue plusieurs rôles : compilateur et éditeur de liens, bien sûr, mais aussi remplaçant de make et des outils similaires, et gestionnaire de paquetages.
Revenons à des programmes zig et voyons ce qui se passe si le programme plante :
const std = @import("std"); pub fn main() void { const name = "myfile.txt"; const f = std.fs.cwd().openFile(name, .{}); std.debug.print("File opened? {any}\n", .{f}); }
Ouvrir un fichier n'est évidemment pas une opération sûre. Le
fichier peut être absent, par exemple. Les différents langages de
programmation ont des mécanismes très différents pour gérer ces cas.
Si vous faites tourner le programme Zig ci-dessus, et que le fichier
myfile.txt
n'existe pas, vous obtiendrez
un File opened? error.FileNotFound. Si le fichier
existe, ce sera File opened? fs.file.File{ .handle = 3,
.capable_io_mode = io.ModeOverride__enum_3788.blocking,
.intended_io_mode = io.ModeOverride__enum_3788.blocking },
une structure de données à travers laquelle on pourra manipuler le
fichier ouvert. La fonction de la bibliothèque standard
openFile
peut donc retourner deux types
différents, une erreur ou une structure d'accès au fichier. Ce
mécanisme est très fréquent en Zig, on peut avoir des unions de type
et les fonctions renvoient souvent « soit ce qu'on attend d'elles,
soit une erreur ».
Maintenant, imaginons un·e programmeur·se négligent·e qui ignore l'erreur et veut lire le contenu du fichier :
var buffer: [100]u8 = undefined; const f = std.fs.cwd().openFile(name, .{}); const result = f.readAll(&buffer); std.debug.print("Result of read is {d} and content is \"{s}\"\n", .{result, buffer[0..result]}); }
Ce code ne pourra pas être compilé :
% zig build-exe erreurs.zig erreurs.zig:8:19: error: no field or member function named 'readAll' in 'error{…FileNotFound…BadPathName…}!fs.file.File' const result = f.readAll(&buffer); ~^~~~~~~~
En effet, en Zig, on ne peut pas ignorer les erreurs (une faute
courante en C). openFile
ne renvoie pas un
fichier (sur lequel on pourrait appliquer
readAll
) mais une union « erreur ou
fichier ». Il faut donc faire quelque chose de l'erreur. Zig offre
plusieurs moyens pour cela. L'une des plus classiques est de
préfixer l'appel de fonction avec try
. Si la
fonction ne renvoie pas d'erreur, try
permet de
ne garder que la valeur retournée, si par contre il y a une erreur,
on revient immédiatement de la fonction appelante, en renvoyant
l'erreur :
var buffer: [100]u8 = undefined; const f = try std.fs.cwd().openFile(name, .{}); const result = try f.readAll(&buffer); std.debug.print("Result of read is {d} and content is \"{s}\"\n", .{result, buffer[0..result]});
Mais ça ne compile pas non plus :
% zig build-exe erreurs.zig erreurs.zig:6:13: error: expected type 'void', found 'error{AccessDenied…}' const f = try std.fs.cwd().openFile(name, .{}); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ erreurs.zig:3:15: note: function cannot return an error pub fn main() void { ^~~~
On a dit que try
renvoyait une erreur en cas de
problème. Or, la fonction main
a été déclarée
comme ne renvoyant rien (void
). Il faut donc la
changer pour déclarer qu'elle ne renvoie rien, ou bien une erreur :
pub fn main() !void {
Le point d'exclamation indiquant l'union du
type erreur et du vrai résultat (ici,
void
). Rappelez-vous : on n'a pas le droit de
planquer les erreurs sous le tapis. Le programme utilisé est
.erreurs.zig
Revenons sur le typage Zig :
Voyons maintenant les variables
optionnelles. Il est courant qu'une variable n'ait pas toujours une
valeur, par exemple si la fonction qui lui donnait une valeur
échoue. En C, et dans d'autres langages, il est courant de réserver
une valeur spéciale pour dire « pas de valeur ». Par exemple 0 pour
un entier, la chaine vide pour une chaine de caractères, etc. Le
problème de cette approche est que cette valeur se trouve désormais
interdite (que faire si l'entier doit vraiment valoir 0 ?) Zig
utilise donc le concept de variable optionnelle, variable qui peut
avoir une valeur ou pas (un peu come le MayBe
d'Haskell). On les déclare avec un
point d'interrogation :
var i:?u8; // = null; var j:?u8; // = 42; std.debug.print("Hello, {d}!\n", .{i orelse 0}); // orelse va déballer la valeur (ou mettre 0) i = 7; std.debug.print("Hello, {any}!\n", .{j}); // Optionnel, donc le format {d} ne serait pas accepté std.debug.print("Hello, {d}!\n", .{i orelse 0});
Ce code ne sera pas accepté tel quel car, rappelez-vous, les variables doivent être initialisées. Autrement, error: expected '=', found ';' (message peu clair, il faut bien l'avouer). En donnant une valeur initiale aux deux variables optionnelles, le programme marche. On notera :
null
indique que la variable n'a pas de
valeur. On peut tester if (i == null)
si on
veut vérifier ce cas.orelse
sert à donner une valeur par
défaut lorsqu'on « déballe » la variable et qu'on découvre qu'elle
n'a pas de valeur.Passons maintenant à un sujet chaud en sécurité (car une bonne partie des failles de sécurité dans les programmes écrits en C viennent de là), la gestion de la mémoire. En simplifiant, il y a les langages qui gèrent tout, laissant le programmeur libre de faire autre chose (comme Python) et les langages où la programmeuse doit gérer la mémoire elle-même. La première approche est évidemment plus simple mais la deuxième permet de contrôler exactement la mémoire utilisée, ce qui est indispensable pour l'embarqué et souhaitable pour les gros programmes tournant longtemps (les serveurs Internet, par exemple). Et l'approche manuelle a également l'inconvénient que les humain·es femelles ou mâles font des erreurs (lire de la mémoire non allouée, ou qui a été libérée ou, pire, y écrire).
L'approche de Zig est que la mémoire est allouée via des allocateurs (concept emprunté à C++) et que différents allocateurs ont différentes propriétés. La responsabilité de la personne qui programme est de choisir le bon allocateur. Commençons par un exemple simple :
pub fn main() !void { var myallo = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = myallo.allocator(); var myarray = try allocator.alloc(u8, 10); // Peut échouer par manque de mémoire defer allocator.free(myarray); std.debug.print("{any}\n", .{myarray}); // Selon l'allocateur utilisé, donnée initialisées ou pas for (0..myarray.len) |i| { myarray[i] = @truncate(i); // @as serait refusé, le type de destination étant trop petit } std.debug.print("{any}\n", .{myarray}); }
Ce programme,
:
allocate.zig
GeneralPurposeAllocator
, allocateur qui
privilégie la sécurité sur les performances, et n'est pas optimisé
pour une tâche précise (comme son nom l'indique),u8
), ce qui peut échouer (d'où le
try
),free
) à exécuter lors de la sortie du bloc
(defer
),@truncate
va
faire rentrer un compteur de boucle dans un octet, quitte à le
tronquer),D'une manière générale, Zig essaie d'éviter les allocations implicites de mémoire. Normalement, vous voyez les endroits où la mémoire est allouée.
La puissance du concept d'allocateur se voit lorsqu'on en
change. Ainsi, si on veut voir les opérations d'allocation et de
désallocation, la bibliothèque standard a un allocateur bavard,
LoggingAllocator
, qui ajoute à un allocateur
existant ses fonctions de journalisation. Comme tous les allocateurs
ont la même interface, définie par la bibliothèque standard, le
remplacement d'un allocateur par un autre est facile :
const myallo = std.heap.LoggingAllocator(std.log.Level.debug, std.log.Level.debug); var myrealallo = myallo.init(std.heap.page_allocator); const allocator = myrealallo.allocator(); var myarray = try allocator.alloc(u8, 10); defer allocator.free(myarray); for (0..myarray.len) |i| { myarray[i] = @truncate(i); } std.debug.print("{any}\n", .{myarray}); const otherarray = try allocator.alloc(f64, 3); defer allocator.free(otherarray); for (0..otherarray.len) |i| { otherarray[i] = @floatFromInt(i); } std.debug.print("{any}\n", .{otherarray});
Ce programme,
va afficher les deux
allocations et, à la fin du bloc, les deux déallocations :
loggingallocator.zig
% ./loggingallocator debug: alloc - success - len: 10, ptr_align: 0 { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } debug: alloc - success - len: 24, ptr_align: 3 { 0.0e+00, 1.0e+00, 2.0e+00 } debug: free - len: 24 debug: free - len: 10
Que se passe-t-il si le·a programmeur·se se trompe et, par
exemple, utilise de la mémoire qui a été désallouée ? Le résultat
dépend de l'allocateur, et des sécurités qu'il fournit (d'où
l'importance du choix de l'allocateur). Par exemple, ce code
(
) :
memoryerrors.zig
var myallo = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = myallo.allocator(); var myarray = try allocator.alloc(u8, 10); for (0..myarray.len) |i| { myarray[i] = @truncate(i); } allocator.free(myarray); std.debug.print("Use after free: {any}\n", .{myarray});
va, avec cet allocateur, provoquer un plantage puisqu'on utilise de la mémoire qu'on vient de désallouer :
% ./memoryerrors Use after free: { Segmentation fault at address 0x7f55f88e0000
Mais si on utilise à la place l'allocateur de la libc (plus rapide mais, comme vous le savez, beaucoup moins sûr), aucune erreur ne se produit (ce qui, en dépit des apparences, est un problème) :
const allocator = std.heap.c_allocator; var myarray = try allocator.alloc(u8, 10); for (0..myarray.len) |i| { myarray[i] = @truncate(i); } allocator.free(myarray); std.debug.print("Use after free: {any}\n", .{myarray}); var myotherarray = try allocator.alloc(u8, 10); allocator.free(myotherarray); allocator.free(myotherarray); std.debug.print("Double free: {any}\n", .{myarray});
On doit le compiler en liant avec la libc (sinon, C allocator is only available when linking against libc) :
% zig build-exe memoryerrors.zig -lc % ./memoryerrors Use after free: { 212, 33, 0, 0, 0, 0, 0, 0, 59, 175 } Double free: { 116, 99, 29, 2, 0, 0, 0, 0, 59, 175 }
En Zig, les bibliothèques qui ont besoin d'allouer de la mémoire
demandent qu'on leur fournisse un allocateur. Si on prend comme
exemple std.fmt.allocPrint
,
qui formate une chaine de caractères, sa documentation précise
qu'elle attend comme premier paramètre une variable de type
std:mem.Allocator
. L'allocateur à usage
généraliste convient donc :
const a = 2; const b = 3; var myallo = std.heap.GeneralPurposeAllocator(.{}){}; const allocator = myallo.allocator(); const mystring = try std.fmt.allocPrint( allocator, "{d} + {d} = {d}", .{ a, b, a + b }, ); defer allocator.free(mystring); std.debug.print("{s}\n", .{mystring});
(Source complet en
.)
allocformatting.zig
Zig permet évidemment d'utiliser des bibliothèques existantes, et d'écrire les siennes. Voyons un exemple avec le DNS. On va utiliser la bibliothèque zig-dns. Elle-même dépend de zig-network, pour envoyer et recevoir des paquets. On les télécharge :
git clone https://github.com/MasterQ32/zig-network.git git clone https://github.com/dantecatalfamo/zig-dns.git
Puis on écrit un programme qui les utilise, pour obtenir
l'adresse IP de www.afnic.fr
:
// On importe les deux bibliothèques const network = @import("zig-network/network.zig"); const dns = @import("zig-dns/src/dns.zig"); // Beaucoup de bibliothèques ont une fonction init, pour… les initialiser. try network.init(); // On crée la prise réseau et on se « connecte » à son résolveur DNS, ici // celui de DNS.sb const sock = try network.connectToHost(allocator, "2a09::", 53, .udp); const writer = sock.writer(); // La requête const message = try dns.createQuery(allocator, "www.afnic.fr", .AAAA); var message_bytes = try message.to_bytes(allocator); try writer.writeAll(message_bytes); // On lit la réponse var recv = [_]u8{0} ** 1024; const recv_size = try sock.receive(&recv); const response = try dns.Message.from_bytes(allocator, recv[0..recv_size]); std.debug.print("Response:\n{any}\n", .{response});
Compilé et exécuté, ce programme nous donne :
% ./use-zig-dns … Response: Message { Header { ID: 1 Response: true … Answers { Resource Record { Name: www.afnic.fr. Type: AAAA Class: IN TTL: 581 Resource Data Length: 16 Resource Data: 2a00:0e00:0000:0005:0000:0000:0000:0002 }
(L'adresse IP de www.afnic.fr
est
2a00:e00:0:5::2
, même si l'affichage par défaut
ne met pas en œuvre la compression du RFC 5952.) Le code complet est en
. Notez que, comme indiqué au
début, Zig est encore expérimental. Les bibliothèques sont peu
nombreuses, pas forcément très documentées, pas toujours bien
testées, et parfois ne compilent pas avec les dernières versions du
langage.use-zig-dns.zig
Zig n'a pas de macros. Les macros qui permettent de changer la syntaxe (comme le préprocesseur C) sont jugées trop dangereuses car rendant difficile la lecture d'un programme. Et celles qui ne changent pas la syntaxe sont moins nécessaires en Zig où on peut faire bien des choses à la compilation. Mais si vous voulez des exemples de tâches qu'on accomplirait avec une macro en C, regardez cette question que j'ai posée sur un forum, avec plusieurs très bonnes réponses.
Zig est fortement typé et un tel niveau de vérification peut
poser des problèmes lorsqu'on veut, par exemple, mettre en œuvre des
structures de données génériques. Par
exemple, une file d'attente FIFO de variables
d'un type quelconque. Zig résout en général ce problème avec
l'utilisation de types pour paramétrer un programme. Ces types comme
variables doivent pouvoir être évalués à la compilation, et sont
donc souvent marqués avec le mot-clé comptime
(compilation time) :
pub fn Queue(comptime Child: type) type { return struct { const This = @This(); const Node = struct { data: Child, next: ?*Node, };
Ici, la file d'attente est programmée sous forme d'un enregistrement
(struct
),
qui comprend des données d'un type Child
quelconque (mais connu à la compilation). Notez qu'une file est créée
en appelant la fonction Queue
qui renvoie un
enregistrement qui comprend, entre autres, les fonctions qui opéreront
sur les files d'attente (permettant de faire partiellement de la
programmation objet). Le code complet est en
(copié du site
officiel).queue.zig
Zig se veut un concurrent de C mais, naturellement, ce n'est pas demain la veille que tout le code existant en C sera recodé en Zig, même dans les hypothèses les plus optimistes. Il est donc nécessaire de pouvoir utiliser les bibliothèques existantes écrites en C. C'est justement un des points forts de Zig : il peut importer facilement des déclarations C et donc permettre l'utilisation des bibliothèques correspondantes. Voyons tout de suite un exemple. Supposons que nous avons un programme en C qui calcule le PGCD de deux nombres et que nous n'avons pas l'intention de recoder (dans ce cas précis, il est suffisamment trivial pour être recodé, je sais) :
unsigned int pgcd(unsigned int l, unsigned int r) { while (l != r) { if (l > r) { l = l - r; } else { r = r - l; } } return l; }
Zig peut importer ce programme (la commande zig
inclut clang pour analyser le C) :
const std = @import("std"); const c = @cImport(@cInclude("pgcd.c")); // No need to have a .h pub fn main() !void { … std.debug.print("PGCD({d},{d}) = {d}\n", .{ l, r, c.pgcd(l, r) }); }
On notera qu'il n'y a pas besoin de déclarations C dans un fichier
.h
(mais, évidemment, si vous avez un
.h
, vous pouvez l'utiliser). Le programme se
compile simplement (et le pgcd.c
est compilé automatiquement
grâce à clang) :
% zig build-exe find-pgcd.zig -I. % ./find-pgcd 9 12 PGCD(9,12) = 3
Pour compiler, au lieu de lancer zig build-exe
,
on aurait pu créer un fichier build.zig
contenant les instructions nécessaires et, après un simple
zig build
fait tout ce qu'il faut (ici,
l'application était suffisamment simple pour que ce ne soit pas
nécessaire). Un exemple d'un tel fichier est
(et le code C est build.zig
et le programme principal pgcd.c
).find-pgcd.zig
Prenons maintenant un exemple d'une vraie bibliothèque C, plus
complexe. On va essayer avec la libidn, qui permet
de gérer des noms
de domaines internationalisés (noms en
Unicode, cf. RFC 5891). On va essayer de se servir de la fonction
idn2_to_ascii_8z
, qui convertit un nom en
Unicode vers la forme Punycode (RFC 5892). D'abord, on va importer l'en-tête fourni
par cette bibliothèque (au passage, n'oubliez pas d'installer les
fichiers de développement, sur Debian, c'est
le paquetage
libidn2-dev
) :
const idn2 = @cImport(@cInclude("idn2.h"));
Puis on crée les variables nécessaires à la fonction qu'on va utiliser :
var origin: [*c]u8 = args[1]; var destination: [*:0]u8 = undefined;
Le type *c
indique une chaine de caractères
sous forme d'un pointeur du langage C, quant à
[*:0]
, c'est une chaine terminée par l'octet
nul, justement ce que la fonction C va nous renvoyer. On peut donc
appeler la fonction :
const result: c_int = idn2.idn2_to_ascii_8z(orig, @ptrCast(&dest), idn2.IDN2_NONTRANSITIONAL);
Ce code peut ensuite être compilé et exécuté :
% ./use-libidn café.fr Punycode(café.fr) = xn--caf-dma.fr (return code is 0)
Comment est-ce qe j'ai trouvé quels paramètres exacts passer à cette fonction ? La documentation conçue pour les programmeurs C suffit souvent mais, si ce n'est pas le cas, si on connait C meux que Zig, et qu'on a un exemple de code qui marche en C, une technique bien pratique est de demander à Zig de traduire cet exemple C en Zig :
% zig translate-c example.c -lc > example.zig
Et l'examen du example.zig
produit vous
donnera beaucoup d'informations.
Notez que je n'ai pas alloué de mémoire pour la chaine de caractères de destination, la documentation (pas très claire, je suis d'accord) disant que la libidn le fait.
Autre problème, le code Zig doit être lié à la libc explicitement (merci à Ian Johnson pour l'explication). Si vous ne le faites pas, vous n'aurez pas de message d'erreur mais (dépendant de votre plate-forme), une segmentation fault à l'adresse… zéro :
% zig build-exe use-libidn.zig -lidn2 % ./use-libidn café.fr Segmentation fault at address 0x0 ???:?:?: 0x0 in ??? (???) zsh: IOT instruction ./use-libidn café.fr % zig build-exe use-libidn.zig -lidn2 -lc % ./use-libidn café.fr Punycode(café.fr) = xn--caf-dma.fr (return code is 0)
Si vous utilisez un build.zig
, n'oubliez donc
pas le exe.linkLibC();
.
Si vous voulez essayer vous-même, voici
et use-libidn.zig
(à renommer en
build-libidn.zig
build.zig
avant de faire un zig
build
). Si vous voulez d'autres exemples, vous pouvez
lire l'article de
Michael Lynch.
Une des forces de Zig est l'inclusion dans le langage d'un mécanisme de tests permettant de s'assurer du caractère correct d'un programme, et d'éviter les régressions lors de modifications ultérieures. Pour reprendre l'exemple du calcul du PGCD plus haut, on peut ajouter ces tests à la fin du fichier :
const expect = @import("std").testing.expect; fn expectidentical(i: u32) !void { try expect(c.pgcd(i, i) == i); } test "identical" { try expectidentical(1); try expectidentical(7); try expectidentical(18); } test "primes" { try expect(c.pgcd(4, 13) == 1); } test "pgcdexists" { try expect(c.pgcd(15, 35) == 5); } test "pgcdlower" { try expect(c.pgcd(15, 5) == 5); }
Et vérifier que tout va bien :
% zig test find-pgcd.zig -I. All 4 tests passed.
Il n'y a pas actuellement de « vrai » programme écrit en Zig, « vrai » au sens où il serait utilisé par des gens qui ne connaissent pas le langage de programmation et s'en moquent. On lira toutefois un intéressant article sur le développement de Bun.
Si ce court article vous a donné envie d'approfondir Zig, voici quelques ressources en ligne :
Première rédaction de cet article le 3 octobre 2023
J'ai visité aujourd'hui les locaux du ministère de l'Intérieur (SDLC, Sous-Direction de Lutte contre la Cybercriminalité, à Nanterre) où sont installées les plate-formes Pharos (signalement de contenus illégaux) et Thesee (gestion centralisée des plaintes pour escroquerie). La visite avait été organisée par le député Renaissance Éric Bothorel.
C'était une visite très cadrée, évidemment. On n'a finalement pas vu la plate-forme Pharos mais elle nous a été décrite comme très proche de celle de Thesee (un grand open space avec des PC : rien de spectaculaire). Donc, pas de démonstration de l'informatique de Pharos.
Pharos est le service derrière
. Leur
rôle est de recevoir des signalements de contenus illégaux par les
internautes (identifiés ou pas), de les qualifier (est-ce vraiment
illégal), de déclencher une procédure judiciaire si pertinent,
d'identifier les auteurs et éventuellement de faire suivre à un
autre service. La plate-forme travaille 24x7 et traite parfois des
cas urgents (annonce d'un suicide, Pharos prévient la
police/gendarmerie la plus proche et intervention immédiate). Pour
identifier les auteurs, Pharos utilise diverses méthodes, notamment
de réquisitions auprès des FAI/hébergeurs/VPN/etc. Évidemment, leur rapidité et leur
efficacité à répondre est très variable. Notez que certains
opérateurs de VPN répondent bien, donc l'utilisation d'un VPN ne
vous rend pas forcément anonyme. Mais on n'a pas eu de chiffres
précis sur le pourcentage de délinquants non identifiés car
utilisant un VPN qui ne répond pas.https://www.internet-signalement.gouv.fr/
Tout contenu illégal est concerné : racisme, terrorisme, appel à la violence, pédopornographie, escroquerie (voir plus loin le rôle de Thesee), etc.
Les signalements peuvent être faits par des citoyens ordinaires, ou par des « signaleurs de confiance », les 84 personnnes morales qui ont signé un partenariat avec Pharos (la liste ne semble pas publique). Ces signaleurs de confiance peuvent signaler plusieurs URL d'un coup, et mettre une pièce jointe (ce qui n'est pas possible pour les signaleurs ordinaires, entre autres pour des raisons de sécurité).
Pour enquêter, les gens de Pharos utilisent les outils classiques de l'Internet plus des logiciels du marché et des développements locaux. Il ne semble pas qu'il existe un logiciel unique, regroupant tout ce dont l'enquêteur a besoin. (Mais je rappelle qu'on n'a pas eu de démonstration.) Pour ne pas être identifié comme la police (un site d'escroquerie pourrait leur servir un contenu différent), ils passent par un abonnement Internet ordinaire, qui n'est pas celui du ministère, et éventuellement par des VPN.
Pharos ne publie pas actuellement de rapport d'activité, avec des chiffres. Toutes les données chiffrées qui circulent viennent d'informations ponctuelles (par exemple de questions écrites à l'Assemblée nationale). Un amendement législatif récent (dû à Bothorel, au nom de l'open data) impose la publication d'un rapport d'activité régulier, avec des données chiffrées. La première édition du rapport est d'ailleurs prête, n'attendant que sa révision RGAA. En 2022, il y a eu 175 000 signalements (on ne sait pas pour combien de contenus, plusieurs signalements peuvent concerner le même), 390 procédures judiciaires ouvertes, 89 000 demandes administratives de retrait, 354 blocages exigés (leur liste n'est pas publique contrairement à, par exemple, celle de l'ANJ).
Le nombre de signalements varie beaucoup avec l'actualité, par exemple lors des troubles suivant la mort de Nahel Merzouk à Nanterre. C'est notamment le cas pour la rubrique « terrorisme », très liée à l'actualité. Seule la pédo-pornographie a un rythme constant.
Pharos ne travaille que sur du contenu public (tweet, site Web), afin de pouvoir le vérifier et le qualifier, donc une insulte raciste sur WhatsApp, par exemple, n'est pas de leur ressort.
Les signalements sont évidemment de qualité variable. Ils peuvent concerner du contenu légal, ou être franchement incompréhensibles. (Les responsables disent qu'ils n'ont pas vu beaucoup de signalements malveillants ; les erreurs sont nombreuses, mais de bonne foi.) Par exemple un type avait fait une vidéo très bizarre (« ambiance et bruitages ») sur la disparition du petit Émile, et elle avait été signalée comme « c'est peut-être lui le coupable ». Pharos ne tient pas compte du nombre de signalements pour un contenu donné, chaque signalement compte.
Pharos emploie entre 40 et 50 personnes (selon les fluctuations, le burn out est un problème, voir plus loin), tou·tes policiers et gendarmes. Il y a un peu de spécialisation : un pôle est spécialisé dans l'ouverture des procédures judiciaires, un autre dans l'expression de la haine en ligne, jugée assez différente pour avoir son propre pôle.
Lors d'un interview à France Inter, la semaine dernière, la présidente du Haut Conseil à l'Égalité, en réponse au journaliste qui lui demandait si elle prônait l'augmentation des effectifs de Pharos pour les nouvelles règles sur la pornographie, avait répondu que ce n'était pas nécessaire car « [le problème] est très facile, il suffit d'utiliser les techniques d'Intelligence Artificielle ». Nos interlocuteurs ont affirmé que Pharos n'utilisait pas du tout d'IA, que peut-être dans le futur, mais se sont montrés plutôt sceptiques, vu la difficulté à qualifier exactement les cas auxquels ielles sont confrontés. L'humain reste irremplaçable, à leur avis.
Sur le problème psychologique, puisque les enquêteurs voient passer pas mal d'horreurs (par exemple les vidéos de Daech) : la politique de Pharos est qu'il n'y a pas de jugement de valeur si un employé n'y arrive plus. « Pas de culture viriliste », « Même les grands et forts peuvent avoir des problèmes ». Les employés ont droit à une aide psychologique.
Un autre pôle à Pharos est celui chargé des mesures administratives (ne passant pas par le contrôle d'un juge). Pharos peut demander à un hébergeur le retrait d'un contenu, et si ce n'est pas fait, ordonner aux grands FAI de bloquer (presque toujours par résolveur DNS menteur). C'est une « erreur humaine » qui a été la cause du blocage de Telegram.
Et Thesee ? Cette plate-forme, plus récente (mars 2022), et que nous avons visitée (comme dit plus haut : un grand open space avec des PC, rien d'extraordinaire), est consacrée à la centralisation de certaines plaintes sur l'escroquerie. (Vous pouvez enregistrer votre plainte via Thesee.) L'idée est que beaucoup d'escroqueries portent sur des petites sommes et que, si on regarde individuellement les plaintes, elles peuvent sembler peu importantes et être mises en bas de la pile de dossiers à traiter. Or, ces « petites » escroqueries peuvent être l'œuvre de gens qui travaillent en grand et escroquent de nombreuses personnes, donc le montant total peut être important. Mais, sans centralisation et regroupement, on peut ne pas voir qu'il ne s'agit pas de manœuvres isolées. Thesee est donc une plate-forme humaine d'analyse de ces clusters. Par exemple, si dix escroqués ont porté plainte contre un faux vendeur et que l'IBAN du vendeur est toujours le même, on a trouvé un cluster. Un logiciel aide à effectuer ces regroupements. (Mais la démonstration de ce logiciel n'a pas fonctionné.) De toute façon, aussi bien pour Thesee que pour Pharos, les enquêteurs ont insisté sur le fait que la technique ne peut pas tout, qu'une enquête est surtout un travail humain.
Thesee compte 32 personnes. Déjà 100 000 plaintes.
Ces escroqueries touchent souvent des personnes vulnérables, et les enquêteurs de Thesee sont souvent confrontés à la détresse des victimes, quand on leur apprend qu'ils ne reverront jamais leurs économies, ou que la jolie femme qui, depuis l'autre bout du monde, leur affirmait qu'elle était tombée amoureuse d'eux et avait juste besoin de 4 000 € pour venir les rejoindre, n'existait pas. Bref, il faut être bienveillant lors des contacts avec les victimes, d'autant plus que l'entourage de celles-ci ne l'est pas toujours (« mais comment as-tu pu être aussi bête ? »).
Pendant la visite. De gauche à droite, Jeanne Bouligny, Éric Bothorel, Pierre Beyssac et moi :
Date de publication du RFC : Septembre 2023
Auteur(s) du RFC : W. Kumari (Google), P. Hoffman (ICANN)
Chemin des normes
Première rédaction de cet article le 30 septembre 2023
Le TLD
.alt
a été réservé pour les utilisations
non-DNS. Si demain
je crée une chaine de blocs nommée Catena et
que j'enregistre des noms dessus, il est recommandé qu'ils se
terminent par catena.alt
(mais comme toujours
sur l'Internet, il n'y a pas de police mondiale chargée de faire
respecter les RFC).
Ce nouveau RFC
est l'occasion de rappeler que noms
de domaine et DNS, ce
n'est pas pareil. Les noms de domaine existaient avant le DNS
et, même aujourd'hui, peuvent être résolus par d'autres techniques
que le DNS (par exemple votre fichier local
/etc/hosts
ou équivalent).
Mais le DNS est un tel succès que cette « marque » est utilisée
partout. On voit ainsi des systèmes de résolution de noms n'ayant
rien à voir avec le DNS se prétendre « DNS pair-à-pair » ou « DNS
sur la blockchain », ce
qui n'a aucun sens. Ce nouveau RFC, lui, vise clairement uniquement
les systèmes non-DNS. Ainsi, des racines alternatives ou des
domaines privés comme le
.local
du RFC 6762 ne
rentrent pas dans son champ d'application. De plus, il ne s'applique
qu'aux noms de domaine ou en tout cas aux identificateurs qui leur
ressemblent suffisamment. Si vous créez un système complètement
disruptif où les identificateurs ne ressemblent pas à des noms de
domaine, ce RFC ne vous concerne pas non plus. Mais si, pour des
raisons techniques (être compatible avec les applications
existantes) ou marketing (les noms de domaine, c'est bien, tout le
monde les reconnait), vous choisissez des noms de domaine comme
identificateurs, lisez ce RFC.
En effet, il existe plusieurs de ces systèmes. Ils permettent, en
indiquant un nom de domaine (c'est-à-dire une
série de composants séparés par des points comme
truc.machin.chose.example
) d'obtenir des
informations techniques, permettant, par exemple, de trouver un
serveur ou de s'y connecter. Un exemple d'un tel système est le
mécanisme de résolution utilisé par Tor. Les
identificateurs sont construits à partir d'une clé
cryptographique et suffixés d'un
.onion
(réservé par le RFC 7686). Ainsi, ce blog est en
http://sjnrk23rmcl4ie5atmz664v7o7k5nkk4jh7mm6lor2n4hxz2tos3eyid.onion/
. N'essayez
pas ce nom dans le DNS : vous ne le trouverez pas, il se résout via
Tor.
Pendant longtemps, cette pratique de prendre, un peu au hasard,
un nom et de l'utiliser comme TLD a été la façon la plus courante de créer des
noms de domaine dans son espace à soi. C'est ainsi que
Namecoin a utilisé le
.bit
, ENS (Ethereum Name Service) le
.eth
et GNUnet le
.gnu
. Chacun prenait son nom comme il voulait,
sans concertation (il n'existe pas de forum ou d'organisation pour
discuter de ces allocations). Cela entraine deux risques, celui de
collision (deux systèmes de nommage utilisent le même TLD, ou bien
un système de nommage utilise un TLD qui est finalement alloué dans
le DNS, ce qui est d'autant plus probable qu'il n'existe pas de
liste de ces TLD non-DNS). Il y a plusieurs solutions à ce problème,
et l'IETF en a longuement discuté (cf. par exemple
l'atelier
EName de 2017, ou bien le très contestable RFC 8244). Ce RFC a donc mis des années à sortir. L'ICANN, par exemple, a essayé de diaboliser ces
noms, attirant surtout l'attention sur leurs dangers. Autre méthode,
il a été suggéré de créer un mécanisme de concertation pour éviter
les collisions, création qui n'a jamais eu lieu, et pas pour des
raisons techniques. Ce RFC 9476 propose simplement de
mettre les noms non-DNS sous un TLD unique, le
.alt
. Ce TLD est réservé dans le registre
des noms spéciaux (créé par le RFC 6761). Ainsi, si le système de résolution de Tor était
créé aujourd'hui, on lui proposerait d'être en
.onion.alt
. Est-ce que les concepteurs de
futurs systèmes de résolution non-DNS se plieront à cette demande ?
Je suis assez pessimiste à ce sujet. Et il serait encore plus
utopique de penser que les TLD non-DNS existants migrent vers
.alt
.
Comme .alt
, par construction, regroupe des
noms qui ne sont pas résolvables dans le DNS, un résolveur purement
DNS ne peut que répondre tout de suite NXDOMAIN (ce nom n'existe
pas) alors qu'un résolveur qui parle plusieurs protocoles peut
utiliser le suffixe du nom comme une indication qu'il faut utiliser
tel ou tel protocole. Si jamais des noms sous
.alt
sont réellement utilisés, ils ne devraient
jamais apparaitre dans le DNS (par exemple dans les requêtes aux
serveurs racine) mais, compte-tenu de
l'expérience, il n'y a aucun doute qu'on les verra fuiter dans le
DNS. Espérons que des techniques comme celles du RFC 8020, du RFC 8198 ou du RFC 9156 réduiront la
charge sur les serveurs de la racine; et préserveront un peu la vie
privée (section 4 du RFC).
Le nom .alt
est évidemment une référence à
alternative mais il rappelera des souvenirs aux utilisateurs
d'Usenet. D'autres noms avaient été
sérieusement discutés comme de préfixer
tous les TLD non-DNS par un tiret
bas. Mon catena.alt
aurait été
_catena
:-) Tout ce qui touche à la
terminologie est évidemment très sensible, et le RFC prend soin de
souligner que le terme de « pseudo-TLD », qu'il utilise pour
désigner les TLD non-DNS n'est pas péjoratif…
On note que le risque de collision existe toujours, mais sous
.alt
. Notre RFC ne prévoit pas de registre des
noms sous .alt
(en partie parce que
l'IETF
ne veut pas s'en mêler, son protocole, c'est le DNS, et en partie
parce que ce milieu des mécanismes de résolution différents est très
individualiste et pas du tout organisé).
Première rédaction de cet article le 27 septembre 2023
Dernière mise à jour le 28 septembre 2023
La faille de sécurité MaginotDNS est une faiblesse de certains résolveurs DNS, qui ne vérifient pas assez les données quand elles sont envoyées par un résolveur à qui ils avaient fait suivre leurs requêtes. Elle peut permettre l'empoisonnement du résolveur par de fausses données.
La faille a été présentée à Usenix, a son propre site Web, et a été désignée par CVE-2021-25220 (pour BIND ; Knot a eu CVE-2022-32983 et Technitium CVE-2021-43105). Elle frappe plusieurs logiciels, BIND (quand on l'utilise comme résolveur), Knot Resolver, le résolveur de Microsoft et Technitium. Elle se produit quand le résolveur fait suivre (to forward) les requêtes DNS pour certains domaines à un autre résolveur (ce que l'article nomme un CDNS, Conditional Domain Name Server). Voici un exemple de configuration de BIND qui, sur une version vulnérable du logiciel, peut permettre la faille :
options { recursion yes; dnssec-validation no; }; zone "unautredomaine.example." { type forward; forwarders { 192.0.2.1; }; };
Cette configuration se lit ainsi : pour tous les noms de domaine sous
unautredomaine.example
, BIND va, au lieu de
passer par le mécanisme de résolution habituel (en commençant par la
racine), faire
suivre la requête au résolveur
192.0.2.1
(une autre configuration, avec
stub
à la place de
forward
, permet de faire suivre à un serveur
faisant autorité ; en pratique, avec certains logiciels, on peut
faire suivre aux deux et, parfois, cela permet d'exploiter la faille).
Évidemment, si on écrit une telle configuration, c'est qu'on
estime que 192.0.2.1
est légitime et digne de
confiance pour le domaine
unautredomaine.example (et ceux en-dessous, les noms de
domaine étant arborescents). Mais la faille
MaginotDNS permet d'empoisonner le résolveur pour tous les
noms à partir de 192.0.2.1
,
ou d'une machine se faisant passer pour
lui.
Le DNS sur UDP (le mode par défaut) ne protège pas contre
une usurpation d'adresse
IP. Une machine peut donc répondre à la place du vrai
192.0.2.1
. Pour empêcher cela, le DNS dispose
de plusieurs protections :
truc.machin.example
et qu'on lui répond par
un enregistrement pour chose.example
, il ne
gardera pas cette information qui est hors-bailliage.
Du fait de la dernière règle, sur le bailliage, un attaquant qui
contrôle 192.0.2.1
ne pourrait que donner des
informations sur les noms sous
unautredomaine.example
.
Mais les chercheurs qui ont découvert MaginotDNS ont repéré une faille dans le contrôle du bailliage : celui-ci est, sur certains logiciels, bien trop laxiste, lorsque la demande a été transmise à un autre résolveur et pas à un serveur faisant autorité. Il est donc possible de faire passer des données hors-bailliage. L'article donne l'exemple d'une réponse qui inclurait l'enregistrement mensonger :
com. IN NS ns1.nasty.attacker.example.
Une telle réponse, clairement hors-bailliage, ne devrait pas être
acceptée. Mais elle l'est (plus exactement, l'était, les logiciels vulnérables
ont tous été corrigés) lorsqu'elle vient du résolveur à qui on a
fait suivre une requête. À partir de là, le résolveur empoisonné
fait passer toutes les requêtes des noms en
.com
à l'ennemi…
L'attaquant a plusieurs moyens d'exploiter cette faiblesse. Je vais en citer deux :
forwarder
(192.0.2.1
dans l'exemple plus haut). Bien sûr, si on décide de faire suivre
à 192.0.2.1
, c'est qu'on lui fait
confiance. Mais on lui fait une confiance
limitée, à
unautredomaine.example
. Avec MaginotDNS,
192.0.2.1
peut injecter des réponses pour
tout domaine. (Cette attaque est appelée
on-path dans l'article mais cette terminologie
est inhabituelle.)forwarder
. Il doit alors répondre à sa place
en essayant de deviner Query ID et port source,
ce qui n'est pas impossible (l'article donne les détails de ce
cas, appelé off-path).Dans les deux cas, l'absence de contrôle correct du bailliage permet à l'attaquant d'empoisonner n'importe quel nom.
Quelles sont les solutions que l'administrateur système peut déployer :
dnssec-validation
no
). En 2023, tout le monde l'a déjà
déployé, de toute façon, non ?Je recommande la lecture du ticket 2950 du développement de BIND, bien plus clair à mon avis que l'article originel (la figure 4 est un modèle de confusion). Et merci à Petr Špaček pour ses explications.
Articles des différentes années : 2024 2023 2022 2021 2020 2019 2018 Précédentes années
Syndication : Flux Atom avec seulement les résumés et Flux Atom avec tout le contenu.