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.


RFC 9421: HTTP Message Signatures

Date de publication du RFC : Février 2024
Auteur(s) du RFC : A. Backman (Amazon), J. Richer (Bespoke Engineering), M. Sporny (Digital Bazaar)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF httpbis
Première rédaction de cet article le 17 janvier 2025


Ce RFC spécifie comment on peut signer cryptographiquement et vérifier des messages HTTP, afin de s'assurer de leur authenticité. Des signatures HTTP conceptuellement proches, mais syntaxiquement différentes sont notamment utilisées par le fédivers.

Alors, attention, vous allez me dire « mais tout le monde utilise HTTPS, de toute façon » mais ce n'est pas tout à fait la même chose. Comme l'explique la section 1 de notre RFC, TLS protège une connexion mais ne fonctionne pas de bout en bout, s'il y a sur le trajet des relais qui terminent une session TLS et ouvrent une autre connexion (la section 7.1.2 détaille les limites de TLS pour les scénarios envisagés). Et TLS ne permet pas d'authentifier le client, sauf à utiliser des certificats client, que beaucoup trouvent peu pratique, et qui dépendent d'un système centralisé, les AC. En prime, dans un contexte d'utilisation serveur-serveur, comme dans le fédivers, on n'a pas envie de confier sa clé privée à son serveur. Donc, il y a un besoin pour une signature des messages (TLS n'est pas inutile pour autant, il fournit notamment la confidentialité, cf. section 8.2 de notre RFC). Voilà pourquoi Mastodon, par exemple, a commencé à utilisé des signatures HTTP pour la sécurité. Le format ActivityPub ne prévoyait en effet aucune sécurité, d'où la nécessité de développer des techniques ad hoc. Mais notez que la technique de Mastodon, non normalisée et même pas spécifiée quelque part, sauf dans le code source, n'est pas compatible avec celle du RFC. Un travail est en cours pour documenter cela.

Notons qu'il existe aussi des solutions pour signer le corps d'un message HTTP, comme JWS (RFC 7515) si le corps est en JSON (RFC 8259). Mais les signatures HTTP de notre RFC 9421 protègent non seulement le corps (quel que soit son format) mais aussi une partie des champs de l'en-tête. Il n'y a pas de solution générale de protection des contenus en HTTP, juste une protection du canal de communication, avec HTTPS.

Donc, le principe est simple : le signeur canonicalise certains champs de l'en-tête (et, indirectement, le corps, via la condensation du RFC 9530), les signe et met la signature dans l'en-tête. Par exemple, avec ce message HTTP (une simple demande de lecture, qui n'a donc probablement pas besoin d'être authentifiée mais c'est juste un exemple) :

GET /9421.html HTTP/1.1
Host: www.bortzmeyer.org
Date:  Fri, 17 Jan 2025 10:53:17 +0100
Content-Type: text/plain
Content-Digest: sha-256=:591b6607e9e257e26808e2ccf3984c23a5742b78defad9ec7b2966ddcef29909=:

Hello, HTTP

La signature ajoutera ces deux champs :

Signature-Input: sig=("date" "host";sf "content-type";sf "content-digest" "@method" "@target-uri" "@authority" "@scheme" "@request-target" "@path" "@query");alg="ecdsa-p256-sha256"
Signature: sig=:0dnCVeCDOJ9fQiLqUXLE8hwBDNpAVlO3TJZgqm/FBJ1x2h5/g5qU20UM87BkJt0iK9vpRRgPF9fmWobf6Y5iYQ==:
  

(« sig » est un identificateur arbitraire, qui sert à faire la correspondance entre une signature et ses paramètres, dans le cas où il y a plusieurs signatures.) Le vérificateur devra récupérer la clé publique (notez que le RFC ne précise pas comment : chaque application utilisant les signatures HTTP devra décrire sa méthode de récupération des clés), canonicaliser et refaire les calculs cryptographiques, validant (ou non) la signature.

Voilà, vous connaissez l'essentiel du RFC, maintenant, place aux détails. D'abord, un peu de terminologie : en dépit de leur nom, les signatures HTTP peuvent être aussi bien de « vraies » signatures, faites avec de la cryptographie asymétrique que des MAC incluant une clé secrète (avec de la cryptographie symétrique, donc). Ensuite, relisez bien le RFC 9651 : les champs structurés dans l'en-tête sont un cas particulier, en raison de la canonicalisation qu'ils vont subir. (Vous avez remarqué le sf dans le champ Signature-Input: plus haut ? Il veut dire Structured Field et indique un traitement particulier.)

Ensuite, la question évidemment compliquée de la canonicalisation. HTTP permet et même parfois impose des transformations du message faites par des intermédiaires (la section 1.3 donne une liste partielle des transformations possibles). Il est très difficile, voire impossible, de définir quels champs de l'en-tête vont être respectés et lesquels vont être modifiés. Il n'y a donc pas de liste officielle des champs à signer, chaque application va indiquer les siens. Dans l'exemple ci-dessus, on signe les quatre champs présents, et on l'indique dans le Signature-Input:. Si un intermédiaire ajoute un champ quelconque (par exemple le Forwarded: du RFC 7239), la signature restera valide. Le récepteur ne devra donc pas considérer ce champ comme authentifié. Même chose pour le corps si on ne signe pas son condensat (RFC 9530). La section 7.2.8 du RFC rappelle l'importance de signer le corps, pour la plupart des cas. Notez également que le Signature-Input: plus haut incluait aussi des composants qui ne sont pas des champs de l'en-tête mais qu'on veut signer, comme la méthode (GET) ou les composants de l'URL. Leur nom est précédé d'un arobase.

Autre problème du monde réel : les programmes n'ont pas un contrôle complet du message HTTP produit. Par exemple, le programme va utiliser une bibliothèque qui va formater les champs à sa manière, en ajouter, etc. C'est pour cela qu'il est important de laisser ouverte la question de la liste des champs à signer. Chaque application choisira.

Les signatures HTTP ne sont pas une solution complète prête à l'emploi : bien des points sont délibérement laissés vides dans le RFC. Une solution complète de sécurité doit donc spécifier :

  • quels sont les champs qui doivent être signés, la section 7.2.3 fournissant des indications sur ce choix (par exemple, Mastodon impose que Date: soit présent et signé, pour détecter les messages trop anciens que quelqu'un essaierait de rejouer, cf. la section 7.2.2),
  • comment récupérer la clé publique d'un correspondant (sur le fédivers, c'est fait en utilisant WebFinger, système normalisé dans le RFC 7033),
  • et les autres exigences diverses spécifiques à cette application (la section 3.2.1 donne une bonne idée de ce qu'elles peuvent être).

Les noms des composants signés (champs de l'en-tête et autres) sont décrits dans la section 2 de notre RFC. Pour un champ, c'est simplement son nom en minuscules (comme "date" dans le Signature-Input: plus haut). Autrement, les noms commencent par un arobase par exemple @method indique la méthode HTTP utilisée. Les méthodes de canonicalisation (complexes, surtout pour les champs structurés !) sont dans la même section.

Les différents paramètres de la signature, comme un identificateur pour la clé utilisée, ou comme la date de signature, peuvent également être inclus dans le Signature-Input:.

Tous ces paramètres forment ce qu'on appelle la base (section 2.5). Celle du message donné comme exemple plus haut est :

"date": Fri, 17 Jan 2025 10:53:17 +0100
"host";sf: www.bortzmeyer.org
"content-type";sf: text/plain
"content-digest": sha-256=:591b6607e9e257e26808e2ccf3984c23a5742b78defad9ec7b2966ddcef29909=:
"@method": GET
"@target-uri": https://www.bortzmeyer.org/9421.html
"@authority": www.bortzmeyer.org
"@scheme": https
"@request-target": /9421.html
"@path": /9421.html
"@query": ?
"@signature-params": ("date" "host";sf "content-type";sf "content-digest" "@method" "@target-uri" "@authority" "@scheme" "@request-target" "@path" "@query");alg="ecdsa-p256-sha256"
  

Ici, elle est relativement courte, la plupart des métadonnées n'étant pas mentionnée.

Une fois la signature générée, le signeur ajoute deux champs à l'en-tête (section 4 du RFC) :

  • Signature-Input: qui indique les paramètres de la signature (liste des champs signés, et optionnellement métadonnées sur la signature),
  • Et Signature:, qui contient la signature elle-même.

Les deux champs sont structurés selon la syntaxe du RFC 9651. (Notez que ces deux champs sont une des différences avec l'ancienne version des signatures HTTP, utilisée dans le fédivers. Cette ancienne version n'avait qu'un champ, Signature:. Un vérificateur qui veut gérer les deux versions peut donc utiliser la présence du champ Signature-Input: comme indication que la version utilisée est celle du RFC. L'annexe A expose cette heuristique, qui figure également dans le projet d'intégration avec ActivityPub.)

Voici un exemple de signature avec des métadonnées (date de signature et identifiant de la clé :

Signature-Input: reqres=("@status" "content-digest" "content-type" \
     "@authority";req "@method";req "@path";req "content-digest";req)\
     ;created=1618884479;keyid="test-key-ecc-p256"
Signature: reqres=:dMT/A/76ehrdBTD/2Xx8QuKV6FoyzEP/I9hdzKN8LQJLNgzU\
     4W767HK05rx1i8meNQQgQPgQp8wq2ive3tV5Ag==:  
  

Notez que notre RFC décrit aussi une méthode pour demander qu'un correspondant signe ses messages : inclure un champ Accept-Signature: (section 5 du RFC).

Les signatures HTTP nécessitent la modification ou la création de plusieurs registres IANA :

  • Les champs Accept-Signature:, Signature: et Signature-Input: ont été ajoutés au registre des champs d'en-tête HTTP.
  • Un registre des algorithmes de signature a été créé. On va y trouver par exemple ecdsa-p256-sha256, que j'ai utilisé dans mon exemple (algorithme ECDSA). On peut y ajouter des algorithmes via la politique « Spécification nécessaire » du RFC 8126.
  • Un registre des métadonnées des signatures a été créé. On y trouve par exemple created (la date de signature) ou keyid (l'identificateur de la clé, selon un schéma de nommage spécifique à l'application). On peut y ajouter des valeurs via la politique « Examen par un expert » du RFC 8126.
  • Un registre des noms de composants a été créé, pour mettre ces noms commençant par « @ », comme @method. On peut y ajouter des noms via la politique « Examen par un expert » du RFC 8126.
  • Un registre des noms des paramètres des composants a été créé. Vous avez déjà rencontré sf, par exemple, qui indique qu'un champ de l'en-tête est un champ structuré et doit donc être traité de manière spéciale. On peut y ajouter des noms via la politique « Examen par un expert » du RFC 8126.

Si vous voulez un article d'introduction :

Questions mises en œuvre, on dispose désormais de bibliothèques pour de nombreux langages de programmation (je ne les ai pas testées) :

Une discussion est en cours pour ajouter ces signatures à curl. Si vous programmez, pour tester votre code, je recommande fortement le service en ligne https://httpsig.org/. Notez qu'il ne vérifie pas le condensat du corps du message. Pour fabriquer les clés pour ce service, si vous voulez faire de l'ECDSA, vous pouvez utiliser les commandes OpenSSL suivantes (oui, il doit y avoir une version plus simplm) :

openssl ecparam -out server.pem -name prime256v1 -genkey 
openssl req -new -key server.pem  -nodes -days 1000 -out server.csr  
openssl x509 -in server.csr -out server.crt -req -signkey server.pem -days 2001      
  

La clé privée sera dans server.pem et la publique dans server.crt.

Concernant l'ancienne version des signatures HTTP, vous pouvez consulter :

Cette technique des signatures HTTP a eu une histoire longue et compliquée. Née chez Amazon, elle avait d'abord été décrite dans un Internet draft, draft-cavage-http-signatures en 2013, et déployée, notamment dans le secteur financier. En 2017, Mastodon avait utilisé la technique telle que décrite dans ce document (forçant le reste du fédivers à s'aligner sur « ce que fait Mastodon »). C'est après l'adoption par le groupe de travail httpbis que le projet avait pris sa forme finale. (Regardez les supports de présentation à l'IETF en 2019 et la vidéo de la réunion.) L'auteur du projet initial avait écrit un bon résumé en 2020, décrivant de l'intérieur comment se passe la normalisation dans l'Internet. Les différences principales avec ce que fait le fédivers :

  • Deux champs au lieu d'un, Signature-Input: et Signature:,
  • Les champs utilisent la syntaxe des champs structurés du RFC 9651,
  • Les métadonnées sont également signées.

Téléchargez le RFC 9421


L'article seul

RFC 9231: Additional XML Security Uniform Resource Identifiers (URIs)

Date de publication du RFC : Juillet 2022
Auteur(s) du RFC : D. Eastlake 3rd (Futurewei Technologies)
Chemin des normes
Première rédaction de cet article le 14 janvier 2025


Il existe tout un ensemble de normes pour assurer la sécurité de documents XML, par exemple les protéger contre la lecture non autorisée, ou bien permettre leur authentification. Ces normes dépendent d'algorithmes cryptographiques identifiés par un URI. Ce RFC met à jour la liste précédente de ces URI (qui était dans le RFC 6931) avec les nouveaux algorithmes et corrige quelques erreurs du précédent RFC.

Ces normes de sécurité de XML étaient à l'origine un travail conjoint de l'IETF et du W3C. C'était par exemple le cas des signatures XML du RFC 3275, du XML canonique des RFC 3076 ou RFC 3741. Elles sont désormais maintenues par le W3C qui a produit des versions plus récentes (par exemple pour les signatures XML, le XML canonique ou le chiffrement XML).

Dans un monde dynamique comme celui de la cryptographie, où les progrès de la cryptanalyse nécessitent des changements d'algorithmes, les normes ne sont pas liées à un algorithme particulier. Elles permettent l'agilité cryptographique (le changement d'algorithme) et il faut donc pouvoir indiquer quel algorithme est utilisé pour signer ou chiffrer un document donné. Pour une norme W3C, on ne s'étonnera pas que l'indication se fait par le biais d'un URI (RFC 3986). Les nouveaux algorithmes commencent désormais par le préfixe http://www.w3.org/2021/04/xmldsig-more# (les anciens algorithmes pouvant avoir d'autres préfixes). Ces nouveaux algorithmes (avec 2021/04 dans leur identificateur) sont relativement rares dans ce RFC : on n'invente quand même pas un bon algorithme de cryptographie tous les jours et la plupart des exemples dans cet article utilisent donc le vieux préfixe. Un exemple récent ? EdDSA (RFC 8032) a l'URI http://www.w3.org/2021/04/xmldsig-more#eddsa-ed25519. Rappelez-vous qu'il s'agit d'URI, pas forcément d'URL et que vous n'obtiendrez donc pas forcément un résultat en pointant votre navigateur Web vers http://www.w3.org/2001/04/xmlenc#sha256 (cf. section 5.1).

Notons que notre RFC 9231 ne prend pas position sur la qualité cryptographique des algorithmes : il fournit un moyen de les désigner sans ambiguïté, c'est tout. Si on veut étudier cette qualité cryptographique, il faut lire d'autres documents (comme le RFC 6194 pour SHA-1).

Un exemple d'un ancien algorithme est SHA-1 pour calculer les condensats cryptographiques. Son URI est http://www.w3.org/2000/09/xmldsig#sha1. Sa sécurité est aujourd'hui sérieusement battue en brèche (cf. RFC 6194). Autre exemple d'un algorithme qui était déjà dans le RFC 6931, SHA-512, identifié par http://www.w3.org/2001/04/xmlenc#sha512 .

Il existe aussi des identificateurs pour les MAC combinés avec une condensation, par exemple http://www.w3.org/2001/04/xmldsig-more#hmac-sha512 (RFC 6234).

Et pour les signatures avec un système à clé publique ? L'identificateur indique l'algorithme de cryptographie asymétrique et celui de condensation, par exemple http://www.w3.org/2001/04/xmldsig-more#rsa-sha256 (voir aussi le RFC 3447). Si on trouve RSA ennuyeux, il existe aussi des identificateurs pour un algorithme à courbes elliptiques (RFC 6090 mais notez ses errata), ECDSA, par exemple http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512.

Enfin, il y a les algorithmes de chiffrement symétrique. Par exemple, Camellia (RFC 3713) sera identifié par http://www.w3.org/2001/04/xmldsig-more#camellia256-cbc. Le plus récent SEED (RFC 4269) sera http://www.w3.org/2007/05/xmldsig-more#seed128-cbc.

Un exemple d'utilisation donné par notre RFC, pour de la cryptographie symétrique :


<EncryptionMethod
    Algorithm="http://www.w3.org/2021/04/xmldsig-more#chacha20">
      <Nonce>0123456789abcdef01234567</Nonce>
      <Counter>fedcba09</Counter>
</EncryptionMethod>

  

Voici pour la cryptographie. Mais les normes de sécurité XML prévoient aussi une étape de canonicalisation avant chiffrement ou signature, et là aussi, il y a plusieurs algorithmes, identifiés par des URI comme http://www.w3.org/2000/09/xmldsig#minimal ou http://www.w3.org/2006/12/xmlc14n11#WithComments.

Quelle est la politique d'allocation dans le registre ? La section 5 décrit celle du W3C (le préfixe http://www.w3.org/2007/05/xmldsig-more# est figé, a priori, on n'y mettra pas de nouveaux algorithmes) et celle de l'IETF : comme il est facile d'obtenir un URI (n'importe qui peut en créer un), la seule question est celle de leur enregistrement. Il se fera après un examen par un expert (voir le RFC 8126 pour les politiques d'allocation IETF) après publication d'un texte décrivant le nouvel algorithme.

Quels changements depuis la version précédente de ce RFC, le RFC 6931 ? L'annexe A les liste. Les principaux, à mon avis, sont :


Téléchargez le RFC 9231


L'article seul

New fediverse instance for my bots

First publication of this article on 11 January 2025


If you use the fediverse, you know that the main bot-hosting instance, botsin.space, closed recently. I document here one or two things about the new hosting of two bots I maintain, one answering BGP requests and one for DNS information.

These two bots are documented in the articles "A Fediverse/Mastodon bot for DNS" and "A Fediverse/Mastodon bot for BGP queries". They were hosted on a dedicated-for-bots instance, botsin.space. This one is now closed so I had to move elsewhere.

This proved to be surprisingly difficult. Many instances no longer accept new inscriptions, or are subject to a (sometimes long) process of registration. Some have policies against bots (or mention they are just "tolerated", something I did not want) or against the use of some specific vocabulary (and I did not want the DNS bot to filter words used in domain names).

But there were also technical issues. Some instances never hosted a bot (more specifically a read-write bot, which will need the ability to read notifications; many bots are write-only) and therefore have surprising issues (one accepted the connections but never sent the heartbeats that the Mastodon API uses to check liveness, another replied with 404 to all requests of the streaming API). And I had no experience with debugging these issues (but now I do). In the end, while I would have prefer to try another instance, I used the instance where my main acount is hosted, mastodon.gougere.fr.

As usual on the fediverse, no instance is perfect (botsin.space was blocking a big instance, mamot.fr, for instance) but at least this time it works. You can now resume sending DNS and BGP queries.

Thanks to the people who maintain instances (it is often an unrewarding task) and let's toot.


L'article seul

Articles des différentes années : 2025  2024  2023  2022  2021  2020  2019  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.