Il a récemment été mis en lumière une vulnérabilité qui pourrait affecter
Ruby, en l'occurrence
[CVE-2014-2734](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-2734).
Toutefois, sur la base d'une analyse détaillée retranscrite ci-dessous,
nous considérons que Ruby **n'est pas** concerné par cette faille de
sécurité.

Cette faille donne à un attaquant la possibilité de contrefaire n'importe
lequel des certificats racine, en modifiant sa signature (ce qui revient
dans les faits à remplacer la clé privée originelle du certificat par une
autre clé, choisie par l'attaquant).

## Démonstration du problème

Ce qui suit est notre analyse de CVE-2014-2734. Nous avons été en mesure de
condenser notre prototype initial en quelques lignes qui démontrent le
cœur du problème :

<figure class="highlight"><pre><code class="language-ruby" data-lang="ruby"><span class="nb">require</span> <span class="s1">'openssl'</span>

<span class="n">forge_key</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">PKey</span><span class="o">::</span><span class="no">RSA</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="mi">2048</span><span class="p">)</span>
<span class="n">raw_certificate</span> <span class="o">=</span> <span class="no">File</span><span class="p">.</span><span class="nf">read</span><span class="p">(</span><span class="s2">"arbitrary.cer"</span><span class="p">)</span>
<span class="n">cert</span> <span class="o">=</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">X509</span><span class="o">::</span><span class="no">Certificate</span><span class="p">.</span><span class="nf">new</span><span class="p">(</span><span class="n">raw_certificate</span><span class="p">)</span>
<span class="n">resigned_cert</span> <span class="o">=</span> <span class="n">cert</span><span class="p">.</span><span class="nf">sign</span><span class="p">(</span><span class="n">spoof</span><span class="p">,</span> <span class="no">OpenSSL</span><span class="o">::</span><span class="no">Digest</span><span class="o">::</span><span class="no">SHA1</span><span class="p">.</span><span class="nf">new</span><span class="p">)</span>

<span class="n">resigned_cert</span><span class="p">.</span><span class="nf">verify</span><span class="p">(</span><span class="n">key</span><span class="p">)</span> <span class="c1">#=&gt; true</span></code></pre></figure>

Il peut effectivement sembler étrange que `X509Certificate#verify` soit évalué
à `true`. Le certificat original peut contenir des métadonnées de type
[Subject Public Key Info](http://tools.ietf.org/html/rfc5280#section-4.1.2.7)
qui pointent vers la clé publique originelle, différente de la clé publique
`forge_key`. Manifestement, la paire de clés (publique/privée) utilisée pour
re-signer le certificat ne correspond plus à la clé publique originelle,
référencée dans le hash de métadonnées. Pourquoi, dans ces conditions, `#verify`
retournerait `true` ?

### À propos de la vérification des clés

`X509Certificate#verify` fait usage de la méthode
[`X509_verify`](https://github.com/openssl/openssl/blob/master/crypto/x509/x_all.c#L74)
d'OpenSSL, une fonction qui délègue en fait à
[`ASN1_item_verify`](https://github.com/openssl/openssl/blob/master/crypto/asn1/a_verify.c#L134).
Ces fonctions vérifient la validité de la signature, étant donnée une clé
publique initiale. Cependant, elles ne vérifieront **pas** si la clé en
question correspond à une éventuelle clé publique référencée *dans* le
certificat. C'est pourquoi la valeur de retour `true` est bien le
comportement attendu pour `X509Certificate#verify`, dans ce cas précis.
Il faut bien comprendre que ne pas effectuer cette « double vérification » ne
remet en aucun cas en cause la sécurité du modèle de confiance X.509.

La section 4.1.1.3 de la RFC 5280 rend explicite le fait que, en calculant la
signature d'un certificat, la CA (*Certificate Authority*) confirme *de facto*
la validité des informations contenues dans le-dit certificat. Bien que ce
principe soit visiblement bafoué dans l'exemple ci-dessus, il n'implique en
fait aucune faille de sécurité, du fait de l'implémentation de la procédure. Un
certificat modifié de cette façon ne saurait être utilisé par un attaquant, à
moins que l'attaque ne soit effectivement en mesure de vous faire explicitement
admettre que le certificat est valide.

## Les risques potentiels

Deux cas sont à considérer :

### Le fait de signer à nouveau un certificat racine

En tant qu'utilisateurs de certificats, nous plaçons une confiance
inconditionnelle dans les certificats *racines*. Quand bien même ils ne
contiendraient pas des informations valides, le fait qu'ils aient été
approuvés publiquement par une autorité de certification les rend digne de
confiance. Ils agissent comment des valeurs pré-configurées dans nos
navigateurs ou nos systèmes d'exploitation : le simple fait de les posséder
leur donne le statut de référence. À ce titre, même OpenSSL ne prend pas la
peine de vérifier la signature de ses propres certificats racines, arguant du
fait qu'ils sont auto-signés (cf.
[X509_V_FLAG_CHECK_SS_SIGNATURE documentation](https://www.openssl.org/docs/crypto/X509_VERIFY_PARAM_set_flags.html)).

À ce titre, un certificat racine re-signé est *immédiatement* considéré comme
digne de confiance, même dans le cas où il contiendrait des informations
erronées (Subject Public Key Info). Ce certificat étant racine, il n'y aucune
différence avec un certificat racine « valide », donc aucune raison de
procéder à une vérification. En principe, n'importe qui peut produire des
certificats racines auto-signés qui correspondent point pour point à un
certificat racine déjà existant, à l'exception bien sûr de la signature. On
comprend alors qu'en jouant sur le fait que nous faisons confiance aux
certificats racines que nous possédons, pour la simple raison que *nous les
possédons*, il devient possible à quelqu'un en capacité de produire un
vrai-faux certificat racine et de le mettre en place chez quelqu'un, de se
créer un vecteur d'attaque important.

### Le fait de re-signer un certificat intermédiaire

Si le fait de re-signer un certificat racine ne représente pas une faille de
sécurité *en tant que telle* dans le modèle de confiance X.509, il en va de
même pour le fait de re-signer un certificat intermédiaire, mais pour une
raison tout à fait différente.

Nous ne possédons en général pas de certificats non-racine en avance. Une
contrefaçon sur ce type de certificat sera par conséquent détectée lors de la
procédure de validation
([path validation procedure](http://tools.ietf.org/html/rfc5280#section-6)). Il
s'agit ici de vérifier la signature de tout certificat non racine en utilisant
la clé publique du certificat d'autorité correspondant. Si le certificat non
racine en cours de vérification a été modifié, sa non conformité sera tout
simplement détectée sous la forme d'une signature non valide. Il s'agit du
modèle de confiance standard.

## Conclusion

En résumé, nous sommes convaincus que `X509Certificate#verify` n'introduit pas
de faille de sécurité d'un point de vue technique. D'autres analyses
indépendantes sont arrivées à
[la même conclusion](https://github.com/adrienthebo/cve-2014-2734/), de sorte
que nous avons décidé de demander le retrait de CVE-2014-2734. Vous pouvez lire
l'analyse complète sur le
[prototype original](https://gist.github.com/emboss/91696b56cd227c8a0c13).
