Date de publication du RFC : Avril 2015
Auteur(s) du RFC : C. Evans, C. Palmer, R. Sleevi
(Google)
Chemin des normes
Réalisé dans le cadre du groupe de travail IETF websec
Première rédaction de cet article le 18 avril 2015
Dernière mise à jour le 19 novembre 2019
Depuis que les piratages de Comodo et DigiNotar ont fait prendre conscience du peu de sécurité qu'apportent les certificats X.509, plusieurs solutions ont été proposées pour combler les failles des autorités de certification. La proposition de ce RFC, HPKP (HTTP Public Key Pinning), consistait à permettre à un client d'épingler (to pin) les clés cryptographiques d'un serveur HTTP utilisant TLS, c'est-à-dire à s'en souvenir pendant une durée spécifiée par le serveur. Ainsi, même si un faux certificat est émis par une AC, il ne serait pas accepté. En pratique, HPKP a été peu adopté et semble aujourd'hui en voie d'abandon.
Un exemple plus récent d'un « vrai / faux certificat » (certificat vrai car émis par une AC reconnue, mais faux car émis sans l'autorisation du titulaire du nom de domaine, et dans l'intention de tromper les utilisateurs) est celui du ministère des finances français. Comme souvent avec les certificats mensongers, celui-ci visait Google (je soupçonne fort que le but était de lire le courrier Gmail des employés, mais, à Bercy, on dément, et on affirme que les buts étaient de pouvoir effectuer un filtrage du contenu sur le Web externe, et de passer les flux à l'antivirus) et c'est ce qui explique que cette compagnie soit très présente dans les forums comme l'IETF où on cherche des solutions aux sérieuses faiblesses de X.509. Parmi les solutions élaborées, il y a DANE, qui s'appuie sur le DNS (RFC 6698), mais Google préfère les certificats à la lumière du jour du RFC 6962 et l'épinglage (pinning) des clés, dans ce tout nouveau RFC 7469.
À noter qu'il existe déjà des en-têtes HTTP permettant au serveur de spécifier au client son comportement, comme le HSTS du RFC 6797. Mais l'épinglage de clés est indépendant de HSTS.
L'épinglage fait partie des techniques TOFU (Trust On First Use) : la première fois qu'un client se connecte au serveur, il ne peut pas savoir si la clé est bonne (il doit se fier à la validation X.509, avec ses limites). À la première connexion, le client HTTP (par exemple un navigateur Web) doit espérer qu'il n'y a pas d'homme du milieu ayant réussi à obtenir un faux certificat (pour cette raison, une attaque persistente, comme celle du ministère des finances, citée plus haut, ne serait pas empêchée par l'épinglage). Aux connexions suivantes, le client HTTP pourra par contre vérifier l'identité du serveur, grâce à la clé épinglée, c'est-à-dire gardée en mémoire. Ce n'est pas idéal (imaginez un homme du milieu ayant un faux certificat pour la première connexion et épinglant ses propres clés, empêchant ainsi toute correction ultérieure !) mais c'est un progrès sur la situation actuelle (valider les clés est un problème difficile en cryptographie). À part des clés partagées à l'avance directement entre les correspondants (ce qui ne passerait évidemment pas à l'échelle pour le Web entier), il n'y a pas de solution parfaitement sûre pour la validation des clés.
La section 2 décrit les détails pratiques. Elle définit un nouvel
en-tête HTTP, Public-Key-Pins:
. Présent dans une
réponse HTTPS, cet en-tête indique au client
HTTP que ce serait souhaitable qu'il épingle, qu'il mémorise, la
clé publique. Le contenu de l'en-tête
Public-Key-Pins:
est le
condensat de la clé publique utilisée pour
TLS, condensat encodé en
Base64 (RFC 4648). L'algorithme de
condensation est indiqué dans la valeur de l'en-tête. Aujourd'hui,
c'est forcément SHA-256 (RFC 6234). La clé est l'encodage DER du
champ subjectPublicKeyInfo
du certificat X.509 (à
noter que les cas des clés brutes du RFC 7250 ou
des clés PGP du RFC 6091
ne semblent pas traités, car elles n'ont pas les problèmes des
certificats X.509). Une directive max-age
est
obligatoire dans l'en-tête HTTP, pour indiquer le nombre de secondes que doit durer
l'épinglage (après quoi le client HTTP « oublie » la clé). Voici un
exemple :
Public-Key-Pins: max-age=604800; pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM="
Il existe aussi deux directives
facultatives. includeSubDomains
indique que
l'épinglage s'applique aussi aux serveurs dont le nom est un
sous-domaine de celui-ci. Si le client se connecte en HTTPS à
bar.example.com
et reçoit un
Public-Key-Pins:
avec
includeSubDomains
, et qu'il se connecte plus
tard à foo.bar.example.com
, il exigera de trouver
la clé épinglée (voir la section 4.2 pour quelques pièges de cette directive). Autre directive facultative,
report-uri
indique à quel
URI le client doit signaler les erreurs
résultant de l'épinglage. En effet, on peut prévoir que, comme avec
toute technique de sécurité, il y aura des problèmes. Par exemple, des
webmestres changeront de clé et oublieront de modifier le
Public-Key-Pins:
. Il est donc important que ces
problèmes soient détectés rapidement. Ce signalement se fera selon la technique
décrite plus loin en section 3. Attention aux conséquences pour la
vie privée ! La très détaillée section 5 couvre
ce risque d'intrusivité. Voici un exemple avec ces deux directives, et
l'utilisation de plusieurs clés (par exemple parce qu'un remplacement
est en cours) :
Public-Key-Pins: max-age=2592000; pin-sha256="E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g="; pin-sha256="LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="; includeSubDomains; report-uri="https://other.example.net/pkp-report"
À noter qu'un deuxième en-tête est défini,
Public-Key-Pins-Report-Only:
, qui a exactement la
même syntaxe, mais une sémantique différente : le client HTTP teste la
clé épinglée et, si elle est fausse, le signale à l'URI indiqué
par report-uri
(qui devient donc obligatoire), mais n'empêche pas l'accès au
serveur. C'est utile pour tester avant le vrai déploiement. Les deux
en-têtes sont désormais dans le registre des en-têtes.
Comme indiqué plus haut, la section 3 décrit la façon dont un
client HTTP peut signaler une erreur de validation. Pour cela, le
client HTTP doit fabriquer un message en JSON
(RFC 8259) et l'envoyer avec la méthode
POST
de HTTP à l'URI
indiqué dans la directive report-uri
. Voici un
exemple d'un tel message (la date est au format du RFC 3339) :
{ "date-time": "2014-04-06T13:00:50Z", "hostname": "www.example.com", "port": 443, "effective-expiration-date": "2014-05-01T12:40:50Z" "served-certificate-chain": [ "-----BEGIN CERTIFICATE-----\n MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\n ... ], "validated-certificate-chain": [ "-----BEGIN CERTIFICATE-----\n MIIEBDCCAuygAwIBAgIDAjppMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT\n ... ], "known-pins": [ 'pin-sha256="d6qzRu9zOECb90Uez27xWltNsj0e1Md7GkYYkVoZWmM="', "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"" ] }
Comme souvent en sécurité, le diable est dans les détails, et c'est
ce qui explique que la section 4, l'obligatoire « Security
Considerations » soit particulièrement longue. Par exemple,
l'épinglage peut avoir des conséquences néfastes si on s'est fait
voler sa clé privée : normalement, on génère une nouvelle clé, un nouveau
certificat et on demande à l'AC de
re-signer. Mais on ne pourra pas l'utiliser car la clé publique
correspondant à la clé volée est toujours épinglée dans des tas de
navigateurs. On peut réduire ce problème en diminuant la durée
d'épinglage. Mais, alors, on réduit aussi la durée de la protection
dans le cas où une AC génère un certificat mensonger. Un compromis est
donc nécessaire. Une approche possible est d'épingler, non pas la clé
du serveur mais une clé intermédiaire dans la chaîne des
certificats. Supposons que la société Example, qui gère
example.com
soit cliente de l'AC SmallAC qui a
elle-même son certificat signé par une AC très largement reconnue,
BigAC. En épinglant la clé de SmallAC, on se protège contre un vol de
la clé privée d'example.com
, et contre un faux certificat, même
émis par BigAC. La société Example reste vulnérable à un faux
certificat produit par SmallAC mais cela diminue sérieusement le
nombre d'acteurs qui peuvent attaquer Example. (Notez que
DANE a les mêmes possibilités : on peut publier
dans le DNS sa propre clé, ou bien celle d'une AC.)
On a vu plus haut qu'on pouvait avoir plusieurs clés dans l'en-tête
Public-Key-Pins:
. Une des utilisations de cette
possibilité est le cas où on a une clé de réserve (backup
pin), non utilisée mais gardée en un endroit différent de la
clé du certificat actuel. Ainsi, si, pour une raison ou pour une
autre, la clé est inutilisable, la clé de réserve peut prendre le
relais immédiatement puisqu'elle est déjà épinglée.
Autre cas où l'épinglage peut avoir un effet néfaste, celui de
l'épinglage hostile. Imaginons un méchant qui obtienne un faux
certificat. Il détourne le trafic (par exemple par un
empoisonnement DNS) et envoie les gens vers son
serveur HTTPS, qui semblera légitime, en raison du faux
certificat. Avec l'épinglage, il peut même prolonger son attaque en
épinglant sa clé. Si le site légitime n'avait pas
été visité avant, ou s'il ne faisait pas d'épinglage, l'attaque peut
réussir, bloquant le client HTTPS sur la clé de l'agresseur pour la
durée max-age
spécifiée par ledit agresseur. Il n'y a
pas de solution miracle pour ce problème. Une possibilité est de
précharger les clés de sites connus dans le navigateur. Une autre est
d'utiliser les certificats au grand jour du RFC 6962. (Et, bien sûr, il y a DANE,
RFC 6698, que Google
prend toujours soin de ne pas mentionner.)
Autres risques, ceux pour la vie privée
(section 5). Un serveur peu sympathique peut utiliser l'épinglage
comme une sorte de cookie
discret. Par exemple, il peut mettre une petite image dans un
sous-domaine, épingler des fausses clés pour ce sous-domaine (une
différente par visiteur) et attendre les signalements à
report-uri
pour identifier un client HTTPS
unique. Des sites différents peuvent même coopérer pour avoir le même
report-uri
, pour suivre un client à travers
plusieurs sites.
À noter aussi que les signalements d'un problème de validation contiennent la chaîne de certificats utilisée. Cela peut permettre d'identifier l'utilisation de faux certificats par telle ou telle organisation. C'est plutôt une bonne chose pour détecter des attaques comme celle effectuée au ministère des finances mais cela ne sera évidemment pas apprécié de ceux qui veulent faire du détournement de trafic.
Comme toujours en sécurité, l'utilisabilité est cruciale (section 7). Lorsqu'un épinglage a eu lieu, et que le trafic est ensuite détourné vers un faux serveur, le client HTTPS (par exemple le navigateur Web) va refuser l'accès à ce faux serveur ce qui, du point de vue de M. Toutlemonde devant son navigateur, est un déni de service. Il va donc falloir bien expliquer à M. Toutlemonde ce qui se passe, pour éviter qu'il n'accuse le site légitime.
Le RFC recommande aussi que l'utilisateur puisse accéder facilement à la liste des clés épinglées et aussi, ce qui me semble plus contestable, qu'il puisse la modifier, par exemple en désépinglant les clés. Cela me semble dangereux car cela peut ouvrir une voie à l'ingénierie sociale (« il y a un petit problème technique, si l'accès vous est refusé, cliquez sur Clear all pinned keys »).
L'annexe A contient un exemple, utilisant
OpenSSL en ligne de commande, pour générer le
Public-Key-Pins:
. J'en ai fait un petit script qui s'utilise ainsi :
% make-pin.sh www.ietf.org ... DHKscLdFrBmZiBGxMdZiyaxp29vnyyPltRS1ZmTF09Y=
Et hop, on n'a plus qu'à mettre Public-Key-Pins:
max-age=604800;
pin-sha256="DHKscLdFrBmZiBGxMdZiyaxp29vnyyPltRS1ZmTF09Y="
dans la réponse du serveur HTTP. (Tom me fait remarquer à juste titre
qu'une clé de secours est obligatoire - section 4.3 - et que donc il
faut toujours au moins deux pin-sha256
. J'ai simplifié.) Les premières versions de
l'Internet-Draft qui a mené à ce RFC utilisaient un
programme en Go, make-pin.go
:
% openssl s_client -servername www.ietf.org -connect www.ietf.org:443 > ietf.pem ... % ./make-pin ietf.pem Hex: 0c72ac70b745ac19998811b131d662c9ac69dbdbe7cb23e5b514b56664c5d3d6 Base64: DHKscLdFrBmZiBGxMdZiyaxp29vnyyPltRS1ZmTF09Y=
À noter que l'annexe B contient quelques conseils pratiques à l'usage des gérants de serveurs HTTPS, sur le bon déploiement de l'épinglage. Par exemple, il est recommandé de commencer doucement, en mode report-only avant de se jeter dans le grand bain et de risquer de bloquer ses clients. Comme avec n'importe quelle technique de sécurité, on risque de se planter soi-même si on ne fait pas attention : n'épinglez pas bêtement !
Question mises en œuvre, Chrome et Firefox avaient déjà de l'épinglage, avec une série de clés mise en dur dans le logiciel (voir la description de Mozilla et leur annonce). Vous pouvez tester ici si votre navigateur gère l'épinglage. Une description de l'épinglage des clés dans Chrome avait été faite par Adam Langley et sa lecture est recommandée (par exemple pour comprendre pourquoi on épingle juste des clés et pas des certificats entiers). Pour le monde Microsoft, voir leur article. Il y aussi un article détaillé sur le concept, chez OWASP, et l'article de Robert Love avec des détails pratiques. Le peu de succès de HPKP fait que Firefox, par exemple, a annoncé son intention de l'abandonner (cf. ticket #1412438.)
Merci à Florian Maury et Kim Minh Kaplan pour leur relecture.
Version PDF de cette page (mais vous pouvez aussi imprimer depuis votre navigateur, il y a une feuille de style prévue pour cela)
Source XML de cette page (cette page est distribuée sous les termes de la licence GFDL)