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.
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 :
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),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),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 :
Accept-Signature:
,
Signature:
et Signature-Input:
ont été ajoutés au registre des champs
d'en-tête HTTP.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. 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.@method
.
On peut y ajouter des noms
via la politique « Examen par un expert » du RFC 8126.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) :
https://github.com/superseriousbusiness/httpsig
,https://imrannazar.com/articles/http-signatures-in-php
,https://docs.rs/crate/http-signatures/latest
,https://pypi.org/project/http-message-signatures/
.
Une discussion est en
cours pour ajouter ces signatures à
curl.
Si vous programmez, pour tester votre code, je recommande fortement le service en ligne
.
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) :
https://httpsig.org/
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 :
https://blog.joinmastodon.org/2018/06/how-to-implement-a-basic-activitypub-server/
et https://blog.joinmastodon.org/2018/07/how-to-make-friends-and-verify-requests/
.Mobilizon.Federation.HTTPSignatures.Signature
et il vient à l'origine de Pleroma.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 :
Signature-Input:
et
Signature:
,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 :
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.
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.