¿Firefox implementa un sistema de certificate pinning?... no del todo

viernes, 5 de septiembre de 2014

Ya hemos hablado de certificate pinning en el blog (e incluso "en directo" en la RECSI en estos días). Chrome era el único navegador que, además de ser el primero, implementaba el "pinning" de serie. Internet Explorer se apoya por ahora precariamente en EMET y Firefox en un addon llamado Certificate Patrol. Pero desde su versión 32, según los titulares, comenzará a "pinear"... pero esto es solo el comienzo y aunque ha sido anunciado a bombo y platillo, no es del todo cierto...

¿En qué quedamos según estos titulares y la prueba técnica?... ¿Firefox 32 suporta Public Key Pinning o no?

Brevemente, el "pineo" de certificados propone que el navegador "recuerde" cuáles son los certificados válidos de una web, para que, en el caso de robo o creación ilegítima (y posterior MiTM), el usuario sea alertado de que no se está conectando al sitio correcto aunque la negociación SSL sea válida. Varios incidentes muy famosos han contribuido a que esta opción sea cada vez más utilizada. Firefox, en su versión 32, ha implementado varias mejoras. Para empezar, ha eliminado de su lista de certificados confiables varios creados solo con claves de 1024 bits. La lista de Firefox es totalmente diferente a la de Windows (y Chrome), y bastante más estricta a la hora de elegir "certificados de confianza", con lo que, con este paso, se vuelve todavía más paranoica. Con respecto al "pinning", lo que ha hecho es complementar lo que ya tenían (HSTS) con el PKP (Public Key Pinning). Pero, cuidado, hay dos "tipos" de PKP y Firefox solo ha implementado una parte... de ahí la confusión. Veamos qué es cada cosa.

HSTS

El estándar HTTP Strict Transport Security o HSTS. Se trata de una especificación que permite obligar a que, en una página, se use siempre HTTPS aunque el usuario no lo escriba en la barra del navegador. Para ello, el servidor que lo desee debe enviarle una cabecera al navegador, del tipo:

Strict-Transport-Security: max-age=16070400; includeSubDomains

La primera vez que visita la web, recuerda que a partir de entonces y durante el tiempo especificado en max-age, debe hacerlo por HTTPS. Poco más que añadir sobre esta tecnología.

Public key pinning dinámico

Esto también son unas cabeceras que permiten a un servidor indicarle al navegador cuáles son sus certificados "habituales" y por cuánto tiempo debe recordarlos. Si alguna vez se produce un ataque de hombre en el medio y suplantación de certificados, el navegador se quejará de que no eran los mismos que recuerda que le indicó el servidor. Así, los dueños de los sitios pueden declarar qué certificados son realmente suyos.

Ejemplo de cabeceras de HSTS y Public Key Pinning sobre HTTP devuelto por una página

¿Y cómo funciona exactamente?

Imaginemos que el servidor A mantiene una cadena de certificados "normal", con su CA1, CAInt, y CRaíz. El administrador es cuidadoso, y decide hacer uso de PKP. Le indica a su servidor o su página que devuelva las cabeceras correspondientes declarando sus certificados. Para ello tiene que calcular la codificación base64 del sha256 del atributo SubjectPublicKeyInfo del certificado codificado con DER (parece difícil pero no lo es). Usar el SubjectPublicKeyInfo (SPKI) permite que el certificado cambie aunque se mantenga la clave pública dentro, y por tanto es más flexible. 

Así que le dice a su servidor web o programa que añada estas cabeceras en sus repuestas HTTP:

Public-Key-Pins: max-age=31536000;
pin-sha256=base64(sha256(SPKI(CA1));
pin-sha256= base64(sha256(SPKI(CAInt);

Donde, en realidad, está "pineando" dos de los tres certificados de su cadena de certificación, y avisando al mundo de que, si alguno cambia en otra visita, es necesario sospechar... excepto el "hoja". En algún momento, un usuario los visita, y el navegador (si implementa el PKP dinámico) los recuerda. Pero imaginemos que en algún momento posterior el usuario es atacado, y visita sin querer A', que resulta pertenecer a un atacante y que usa otros tres certificados diferentes (CA1', CAint', y CRaíz') en su cadena de certificación. El navegador de la víctima, antes de establecer comunicación ni mostrar nada de ese servidor, se conecta y se trae los certificados de A'. Calcula los hashes de la cadena, y se mueve por esta regla:

"Si los pines (hashes que representan a los certificados) de la cadena de certificados del sitio que visito, hace intersección con los hashes que recuerdo que me envió hace tiempo, todo está correcto. Si no, mostraré una alerta".

Fallo de pinning en Firefox.
Fuente: http://monica-at-mozilla.blogspot.com.es/2014/08/firefox-32-supports-public-key-pinning.html

O sea, algún elemento de estos dos conjuntos:

  • base64(sha256(SPKI(CA1)), base64(sha256(SPKI(CAInt))
  • base64(sha256(SPKI(CA1')), base64(sha256(SPKI(CAInt')) , base64(sha256(SPKI(CRaíz'))

Debe ser común para que no haya problemas. En resumen, todo va bien si el conjunto de certificados de A (legítimo) intersecta con el de A' (atacante).

A partir de aquí, cabe reconocer que hay escenarios poco probables pero posibles para eludir la seguridad de esta solución, y que el dueño del servidor debe tener cuidado para no generar demasiados falsos positivos. PKP, por ejemplo, no protege del compromiso de claves privadas de certificados intermedios, por ejemplo, puesto que la intersección se daría en muchos casos. Si el administrador del sitio es muy paranoico y "pinea" solo el certificado hoja, el ataque es mucho menos probable, pero se eleva la cantidad de potenciales falsos positivos. Así que habitualmente, lo que hacen es (además está muy recomendado por el RFC) meter un certificado de respaldo para no "bloquearse" por si envían una cabecera que obliga a recordar a los navegadores un certificado durante demasiado tiempo pero poco después expira o se invalida por lo que sea.

PKP "incrustado"

Pues simplemente, es lo mismo que ya se ha explicado pero en este caso los pines (la codificación en base64 del sha256 del SPKI del certificado)  no los envía dinámicamente el servidor, sino que son incrustados en el código del navegador. Así los "recuerda", y el tiempo durante el que los recuerda viene determinado por actualizaciones del programa. Esto no es ningún problema en sí, solo que a largo plazo es insostenible y proporciona muy poca flexibilidad y potencia. Y esto es lo que ha hecho, en su versión 32, Firefox.

Conclusiones

En rigor, es cierto que Firefox sí ha implementado Public Key Pinning, pero no Public Key Pinning sobre HTTP. Eso es algo que se menciona en la nota de prensa como posibilidad, pero no queda totalmente claro que será algo que se hará a futuro. Y si nos fijamos no solo en que en la versión 32 ha hecho un PKP "no dinámico" sino que además ha "pineado" solo una pequeña parte de dominios importantes... la cosa queda en un comienzo prometedor, pero nada definitivo para Firefox como los titulares parecen comunicar.

Y es que en esta versión 32, solo se "pinean" (incrustados) los dominios de Twitter y algunos de la propia Mozilla. En el futuro (quizás en su versión 33) meterán en el código los certificados de Google, Dropbox y en definitiva, todos los que ya implementa Chronium en su código a partir de la versión 34, que no son pocos.

En resumen, los titulares inducen a error si no se especifica que el PKP será "built in" (incrustado) y que por ahora se incrustan muy pocos dominios. El objetivo final, más adelante (en un futuro no determinado) es implementar PKP dinámico, y es el camino que ahora comienza Firefox... la noticia no es, precisamente, que lo haya concluido.

Sergio de los Santos
ssantos@11paths.com

1 comentario: