Je suis Charlie

Autres trucs

Accueil

Seulement les RFC

Seulement les fiches de lecture

Mon livre « Cyberstructure »

Ève

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.


La faille DNSSEC KeyTrap

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 :


L'article seul

IETF 119 hackathon: compact denial of existence for DNSSEC

First 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:

  • In the list of record types that are present at the requested name (the "NSEC bitmap"), a pseudo-type NXNAME, signaling that the NOERROR return code is actually a NXDOMAIN. It is not officially allocated today and implementations use a value reserved for experimentations, 65283 (hence the TYPE65283 above). This can be used by resolvers to restore the NXDOMAIN from the response so that their clients will have a proper error code.
  • EDNS signaling that the client understands compact answers and can receive a NXDOMAIN, it won't be surprised to see the NSEC claiming that the name does not exist. This signaling is done with a bit called CO (for Compact OK).

During the IETF hackathon, two authoritative name servers were modified to generate CDE:

  • adns_server, written in Python.
  • Drink, written in Elixir. The code is currently in a separate branch, 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:

  • Since the draft says that explicit requests for a NXNAME record are forbidden, which error code should be returned and, if using EDE (Extended DNS Errors, RFC 8914), which EDE? (Actually, the problem is larger than just CDE since it seems that the replies to requests of pseudo-types - those between 128 and 255 - are underspecified. May be another work to start.)
  • When a DNS client does not set the DO bit, indicating it does not know about DNSSEC, should we return NXDOMAIN, forgetting about CDE, like most implementations do, or should we return NOERROR, as if the DO bit were set?
  • This is not only about the non-DNSSEC clients: some people are not happy that, by conflating NXDOMAIN and NOERROR, we lose information, and that will make debugging more difficult (unless you have a NXDOMAIN-restoring resolver, which does not exist today). It is important to remember that it is not because something is simple to implement and works, that it is a good idea.

Thanks to Shumon Huque for code, conversation and explanations.


L'article seul

Fiche de lecture : Le libre pensée dans le monde arabo-musulman

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).


L'article seul

Fiche de lecture : Atlas du numérique

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


L'article seul

Fiche de lecture : The Wager

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).


L'article seul

Fiche de lecture : Penser la transition numérique

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é.


L'article seul

Panne du domaine national russe

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 :

  • Du fait du caractère arborescent du DNS, une panne de .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.
  • Comme tous les résolveurs DNS ne valident pas les signatures DNSSEC, la résolution marchait encore pour certains.

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 : telegram-min-russe-dnssec.png

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

Et DNSviz confirme le diagnostic : dnsviz-russia.png

Comme le note DNSviz, les signatures étaient invalides : dnsviz-russia-sig.png

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) :

  • 2024-01-30 12:29:44 UTC : dernier test où tout était correct. Le numéro de série était 4058855, la clé signant les enregistrements (ZSK, Zone Signing Key) était la 44301, la ZSK 52263 était déjà publiée (remplacement de ZSK en cours).
  • 2024-01-30 15:27:27 UTC : premier test avec la panne, qui a donc dû commencer quelques minutes plus tôt (en cas de problème DNSSEC, il y a toujours quelqu'un qui se précipite pour faire un test DNSviz). Le numéro de série est le 4058857. La clé signante est désormais la 52263, et les signatures sont invalides.
  • Il y aura une zone de numéro 4058858, mais le problème continue.
  • 2024-01-30 17:59:46 UTC : la réparation commence. L'ancienne zone de numéro 4058856 est republiée, signée par l'ancienne clé 44301. Notez que, pendant plus d'une heure, plusieurs versions de la zone coexisteront (avec trois numéros de série différents, 4058856, 4058857 et 4058858).
  • 2024-01-30 19:07:29 UTC : réparation terminée, on est revenu à la situation d'avant la panne.
  • À un moment dans la journée du 31 janvier : la zone bouge à nouveau (le numéro de série augmente, la zone est signée par la clé 52263).

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é.


L'article seul

Jouons et sécurisons avec une clé FIDO2/WebAuthn

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 :

  • La difficulté d'avoir des mots de passe à la fois solides (résistant aux attaques par force brute) et mémorisables par l'utilisateurice.
  • Le risque de hameçonnage, lorsque l'utilisateurice se laisse convaincre d'aller sur un service qui n'est pas le vrai, à qui ielle donnera le mot de passe, que le hameçonneur pourra ensuite utiliser.
  • Plus généralement, la trop grande facilité avec laquelle un mot de passe se partage, par exemple suite à de l'ingénierie sociale.

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

  • L'utilisateurice, M. ou Mme Toutlemonde.
  • Le service auquel ielle veut se connecter (par exemple un site Web).
  • Le logiciel client de l'utilisateurice (par exemple un navigateur Web).
  • L'authentificateur, qui est un dispositif à la disposition de l'utilisateurice (par exemple une clé physique comme ma Nitrokey).

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 https://webauthn.io/. Je visite le site Web avec Firefox, j'indique mon identificateur puis je clique sur Authenticate : webauthn-io-firefox.png

Je sors ensuite ma clé, une Nitrokey 3A NFC : nitrokey-3a-nfc.jpg

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). android-webauthn.png

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 : proton-2fa-fido.png

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 :


L'article seul

Gestion de son serveur de courrier électronique

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/, https://sendersupport.olc.protection.outlook.com/snds/ (le programme SNDS - Smart Network Data Services - ne fait qu'afficher, il ne modifie pas les listes de blocage) ou https://olcsupport.office.com/ (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 à 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.


L'article seul

Du nouveau dans la (l'in)sécurité de l'Internet ?

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 : cloudflare-orange-espagne.png

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 : ripe-orange-espagne-as.png

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 : twitter-orange-espagne.png

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 : twitter-orange-espagne-ressources.png

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 :


L'article seul

La ferme de compilation Cfarm

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.


L'article seul

Fiche de lecture : La réputation

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.


L'article seul

Modifier un message entrant en Python

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.
  • Si l'expression rationnelle correspond au motif donné, on ajoute la version modifiée, sinon on ne touche à rien.
  • set_content() remplace l'ancien contenu par le nouveau.
  • Et enfin, 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 :

  • Plusieurs « crypter » sur une ligne.
  • Remplacer la concaténation + 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).

L'article seul

Capitole du Libre 2023, et mon exposé sur la censure de l'Internet

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. capitole-du-libre-2023-garonne.jpg

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 : capitole-du-libre-2023-pains-chocolat.jpg

Personnellement, j'ai fait un exposé sur la censure sur l'Internet. Les supports au format PDF sont capitole-libre-2023-censure.pdf (et leur source en capitole-libre-2023-censure.tex). 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.)

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) :

  • la licence utilisée, bien sûr,
  • le langage de programmation utilisé,
  • les dépendances de cette bibliothèque,
  • le nombre de tickets, s'il est faible, c'est peut-être qu'il n'y a pas beaucoup d'utilisateurs, si beaucoup sont fermés, cela peut être bon signe (bogues corrigées rapidement) ou mauvais signe (mainteneur qui ferme tout de suite sans corriger),
  • la date du dernier ticket fermé peut être un bon indicateur pour savoir si le projet vit toujours,
  • nombre de commits (pas très pertinent car il dépend du style de travail des programmeurs, certain·es font dix commits dans la matinée, d'autres un par semaine (pensez à git rebase…),
  • date du dernier commit pour détecter les codes abandonnés (ou bien tellement parfaits qu'ils n'ont pas besoin de changements…),
  • l'activité du mainteneur ; est-ce qu'il est actif (voir son profil sur la forge publique utilisée) avec le langage du projet (un projet Go créé par quelqu'un qui ne fait que du Rust aujourd'hui risque de de ne pas faire l'objet de beaucoup d'attention),
  • l'existence de tests, et la couverture du code par la suite de tests (pour avoir une base saine si on va devoir modifier le logiciel).

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. chatgtp-altman.png

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 : capitole-du-libre-2023-benevoles.jpg


L'article seul

Fiche de lecture : Ni Web, ni master

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é.


L'article seul

Fiche de lecture : Ada & Zangemann

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 : ada-zangemann.png

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.


L'article seul

Mes débuts en programmation Zig

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 hello.zig. En décommentant les lignes qui commencent par deux barres obliques, vous découvrirez en outre que :

  • Les variables doivent être utilisées (sinon, le compilateur dira error: unused local constant),
  • on ne rigole pas avec le typage. Par exemple, vous ne pouvez pas ajouter un entier à un pointeur (error: incompatible types).

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 :

  • Il est strict, on ne peut pas ajouter un entier à un pointeur, par exemple,
  • il est entièrement statique (les types ne survivent pas à la compilation),
  • résultat (qui ne surprendra pas les programmeurs et programmeuses Rust), arriver à compiler le programme est souvent long et pénible mais, une fois que ça compile, il y a beaucoup moins de bogues qui trainent (ce qui est, entre autre, positif pour la sécurité),
  • les types sont aussi des valeurs, on peut les mettre dans des variables, les passer en paramètres à des fonctions (ce qui est utile pour la généricité) mais tout cela est fait à la compilation.

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 :

  • crée un allocateur à partir d'une fonction de la bibliothèque standard, 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),
  • alloue de la mémoire pour dix octets (u8), ce qui peut échouer (d'où le try),
  • enregistre une action de libération de la mémoire (free) à exécuter lors de la sortie du bloc (defer),
  • écrit dans cette mémoire (les fonctions dont le nom commençe par un arobase sont les fonctions pré-définies du langage ; ici, @truncate va faire rentrer un compteur de boucle dans un octet, quitte à le tronquer),
  • affiche le résultat.

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, loggingallocator.zig va afficher les deux allocations et, à la fin du bloc, les deux déallocations :

  
% ./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 use-zig-dns.zig. 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.

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 queue.zig (copié du site officiel).

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 build.zig (et le code C est pgcd.c et le programme principal 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 use-libidn.zig et build-libidn.zig (à renommer en 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 :


L'article seul

Visite des plate-formes Pharos et Thesee

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 https://www.internet-signalement.gouv.fr/. 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.

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 : visite-thesee.jpeg


L'article seul

RFC 9476: The .alt Special-Use Top-Level Domain

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é).


Téléchargez le RFC 9476


L'article seul

MaginotDNS, une faiblesse de certains résolveurs DNS

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 :

  • L'attaquant qui va répondre à la place du serveur légitime doit deviner le Query ID de la requête,
  • Et (depuis l'attaque Kaminsky) le port source,
  • Et de toute façon le résolveur n'acceptera que les réponses qui sont dans le bailliage (in-bailiwick) de la question. S'il a demandé 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 :

  • La plus évidente est de contrôler la machine listée 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.)
  • Sans doute plus réaliste est le cas où l'attaquant ne contrôle pas la machine listée 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 :

  • Bien sûr mettre à jour ses logiciels (la faille a été bouchée dans tous les programmes vulnérables). Il faut le répéter : les logiciels doivent être maintenus à jour. Aucune excuse ne doit être admise.
  • DNSSEC, qui avait justement été conçu pour cela, protège complètement (c'est pour cela que la configuration vulnérable, plus haut, a dnssec-validation no). En 2023, tout le monde l'a déjà déployé, de toute façon, non ?
  • Pour le deuxième cas, celui où l'attaquant ne contrôle pas le résolveur à qui on fait suivre, toute méthode qui empêche l'usurpation de réponse protégera : DoT (RFC 7858, très recommandé quand on fait suivre une requête), les cookies (RFC 7873), TCP (RFC 7766), TSIG (RFC 8945)…

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.


L'article seul

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.

Un article de ce blog au hasard.