Recientemente fuimos informados de una posible vulnerabilidad de seguridad
que ha sido publicada como
[CVE-2014-2734](http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2014-2734).
Sin embargo, basados en un análisis detallado, **no consideramos** que Ruby
sea vulnerable.

Esta vulnerabilidad podría permitir a cualquier atacante a crear certificados
raíz arbitrarios modificando la firma de el certificado y remplazando la llave
privada del certificado original con cualquier otra elegida por el atacante.

## Prueba de el concepto

A continuación veremos nuestro análisis de CVE-2014-2734 en una versión reducida,
pero que creemos captura la esencia de el análisis original:

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

Puede que sea una sorpresa que `X509Certificate#verify` regresa `true`.
El certificado original puede contener
[información de la llave pública](http://tools.ietf.org/html/rfc5280#section-4.1.2.7)
que apunta a la llave pública original la cual es diferente a la llave pública
de `forge_key`. Claramente, el par público / privado que fue usado para firmar
de nuevo el certificado no concuerda con la llave pública referenciada en
la información original. Entonces, ¿Por qué `#verify` regresa `true`?

### Como es que las llaves son verificadas

`X509Certificate#verify` utiliza la función de OpenSSl
[`X509_verify`](https://github.com/openssl/openssl/blob/master/crypto/x509/x_all.c#L74)
internamente, la cual delega a
[`ASN1_item_verify`](https://github.com/openssl/openssl/blob/master/crypto/asn1/a_verify.c#L134).
Estas funciones establecen la validez de la firma de acuerdo a la llave pública
que fue presentada. Sin embargo, **no verifican** si la llave concuerda con alguna
llave pública referenciada en el certificado.
Esto significa que regresar `true` es comportamiento deseado para `X509Certificate#verify`
en este escenario. Omitir esta verificación no tiene impacto significante en el
modelo de seguridad de confianza X.509.

La sección 4.1.1.3 del RFC 5280 explícitamente dice que al procesar la firma
de algún certificado, la CA confirma que la información que contiene el certificado
sea correcta. Aunque este principio es violado en el código de ejemplo, esto no
presenta una amenaza a la seguridad. Un certificado falso o modificado de esta manera
no puede ser explotado a menos que alguien pueda convencerte de que explícitamente
confíes en un certificado que viola este principio.

## Riesgos Potenciales

Existen dos casos a considerar:

### Volver a firmar un certificado de raíz

Como usuarios, confiamos en los certificados de raíz incondicionalmente. Incluso
si no contienen información válida, el estado de ser un certificado de raíz
reconocido públicamente es lo que los mantiene originales. Son valores
preconfigurados en los almacenamientos de confianza de nuestros navegadores
y sistemas operativos. Simplemente teniendo acceso a ellos establece su estado
como anclas válidas de confianza. Por ejemplo, OpenSSL no revisa la firma de los
certificados de raíz firmados por uno mismo por defecto por las mismas razones.
[X509_V_FLAG_CHECK_SS_SIGNATURE documentation](https://www.openssl.org/docs/crypto/X509_VERIFY_PARAM_set_flags.html).

Un certificado de raíz que ha sido firmado de nuevo, se convierte por defecto
en un certificado "auto-firmado" (por lo tanto con información
incorrecta de llave pública). Esto no es más peligroso que un certificado de raíz
"auto-firmado" normal. De hecho, cualquiera puede crear certificados
de raíz auto-firmados los cuales pueden concordar con aquel de un certificado
de raíz valido, excepto por la firma. Como confiamos en los certificados de
raíz simplemente por poseerlos, cualquier certificado impostor carece de valor
sin que el cliente activamente decida confiar en el.

### Firmado de un certificado intermedio

Tampoco volver a firmar un certificado que no es de raíz viola la seguridad
de el sistema de confianza X.509. Aunque no tenemos en nuestro poder
este tipo de certificados por adelantado, su validez puede ser comprobada durante
el [proceso de validación](http://tools.ietf.org/html/rfc5280#section-6).
La firma de cualquier certificado que no es de raíz es verificada utilizando
la llave publica de el certificado que lo expidió. Si este es falso, en algún momento de la
cadena de certificados será detectado como un valor de firma de certificado
inválida.

## Conclusión

En conclusión, creemos que `X509Certificate#verify` funciona como es debido.

Otros han llegado a la
[misma conclusión](https://github.com/adrienthebo/cve-2014-2734/)
y por lo tanto hemos disputado el CVE-2014-2734 y pedido su cancelación.
Puedes encontrar nuestro análisis completo de
[la prueba de concepto original](https://gist.github.com/emboss/91696b56cd227c8a0c13)
aquí incluyendo algunos comentarios adicionales.
