Método de login «seguro» sin SSL

El objetivo de esta entrada es mostrar una manera de hacer un poco más seguro el acceso a una aplicación web (login) si ésta no dispone de SSL (HTTPS). Para los que son duchos en la seguridad, puede que esto les pueda parecer simple, e incluso obvio, pero en muchas empresas de desarrollo de páginas web, ni se plantean lo peligroso que puede llegar a ser el envío de credenciales en claro a través de Internet.

Un problema que nos encontramos los que nos dedicamos al desarrollo de software, y en concreto de páginas web, suele ser que el cliente no quiere (o no puede) pagar un certificado de servidor para su portal. Lo que tampoco queremos es que nuestras contraseñas vayan por la red en claro, siendo vulnerables a cualquiera que ande “esnifando” por la red. Supongo que existen varias soluciones a este problema, yo os voy a contar una que no es solución y otra que es un poco mejor (en temas de seguridad nunca me atrevería a decir que es infalible, porque creo que nada lo es). Para los ejemplos voy a usar como usuario a bob y como contraseña bob123.

Lo que NO es una solución

Si estáis tratando de solventar este problema, probablemente os encontraréis con sitios que os dicen que la solución pasa por cifrar la contraseña con Javascript (aprovecho para recordar que el verbo encriptar no existe, y cada vez que alguien la usa se muere un gatito) justo antes de que el formulario se mande.

Normalmente suelen decir que hagas un hash de la contraseña y así esta no viajará en claro por la red. Esto no es una solución porque si yo soy un “hacker” maligno y consigo ver la comunicación, no podré leer la contraseña bob123, pero si que veré 2acba7f51acfd4fd5102ad090fc612ee, que es el resultado de aplicar una función hash (md5) a bob123.

La cosa es que a mí, como atacante, me da lo mismo ver lo primero que lo segundo, ya que sé que si mando la petición de login a la página “web” poniendo como usuario bob y como contraseña 2acba7f51acfd4fd5102ad090fc612ee voy a poder acceder a la aplicación (entiéndase que esto se realizará usando alguna herramienta de manipulación de peticiones HTTP como burp o tamperdata etc…). Además, es muy común que las contraseñas en las bases de datos se guarden cifradas con este mismo método (y el que las guarde en claro, tiene un problema muy grave), con lo que, en realidad, puede que lo que esté viajando por la red sea la contraseña en si misma.

Una posible solución

La solución que creo más segura es la de «simular» la conexión SSL mediante un par de claves pública/privada por petición. De esta manera el servidor mandará la clave pública, que le servirá al navegador para cifrar la información, y guarda la clave privada, que sólo sirve para descifrar esa información. Para realizar esto haremos uso del Javascript de forma similar a la anterior. La diferencia es que esta vez cuando el cliente (navegador web) haga la petición a la pantalla de login, el servidor generará un par de claves nuevas.

Creo que es de buena praxis cifrar, además de la contraseña, el nombre de usuario ya que cuantas menos pistas demos a los atacantes mejor. Así pues, cada vez que le mandemos la información de acceso al servidor, ésta será diferente, y aunque vieran que el usuario es 0a42b6b9dcd569f990d y la contraseña es cde40f4ff73c5a24eb904, esto sólo será válido una vez ya que a cada petición el servidor nos manda una clave pública distinta. Hay que tener mucho cuidado con esto, dado que si el servidor usa como semilla de generación de claves algo obvio, como el tiempo, se podría mediante repetidas peticiones de la página de login, obtener el algoritmo que esta usando para generarlas y podría llegar a obtener la clave privada para una petición y descifrar así la información.

Comments

  1. Buen post. Sólo quería pedirte una corrección: no se dice «contra menos pistas demos», sino «cuantas menos pistas demos».

  2. Lo habitual es que el javascript haga el hash con el password concatenando una challenge que genera el servidor (desafio-respuesta), de esta manera el cliente envia un hash que valida el password, pero solo para esa sesión, impidiendo que alguien capture el hash para la sesión. El metodo ademas de sencillo creo que es bastante robusto teoricamente. El desafio suele ser en estos casos un timestamp del servidor.

    Lo que propones tiene un alto coste computacional, tanto para el cliente como para el servidor, ademas de la complejidad que tu mismo comentas de generar numeros aleatorios con garantias.

  3. Chous, corrijo lo que indicas, gracias :)
    Damià, a eso te contestará Guillermo.

  4. 1) Gracias por la corrección chous.
    2) Estas seguro que cuesta tanto? Cierto es que el servidor (y encargado de generar el par de claves) si tiene cierto coste, pero creo que al cliente no le supone mucho «esfuerzo computacional». Por poner un ejemplo: http://www.fourmilab.ch/javascrypt/jscrypt.html
    De todas maneras, trataré de realizar una prueba de concepto y mostrar los tiempos. Muchas gracias por la observación.

  5. El método de javascript con hash de desafío respuesta + password, creo que es muy eficiente y robusto.
    Pero se me ocurre un caso en el que se podría emplear lo que comentas, y esto seria en el proceso de registro, donde si que hay que enviar la contraseña (o el hash de esta). Entonces si que seria útil montar lo de la clave privada/publica en javascript. En todo caso generar las claves lo sigo viendo «caro», igual podría enviarse con una clave privada ya pregenerada y así no machacas la CPU del servidor.

  6. ¿Podrías detallar un poco más la posible solución por favor?

  7. Al precio que estan los certificados SSL no merece la pena.

  8. Como siempre Guillermo, felicidades por el artículo, sencillo y ¡¡¡breve!!! muy breve, tanto que me he quedado con las ganas justo cuando entraba en materia. :(

    Saludos

  9. Pues como dice pirri, los certificados no son tan caros, pero los hay que no quieren pagar por ello. Como ya le dije a Damia, preparo un ejemplo y así queda más claro y podemos ver el coste real.
    Un saludo a todos y gracias por leer!

  10. Puedo interceptar las comunicaciones y cambiar la clave pública del servidor por una mia, descifrar y enviar de nuevo los datos cifrados al servidor con la clave pública legítima… MITM

  11. Por ese motivo «seguro» y no SEGURO! Gracias por tu apreciación. A partir de ahi, podemos jugar con tokens. De todas maneras, si el sitio es importante, seguro que se hacen con un certificado, que como dice pirri, «Al precio que estan los certificados SSL no merece la pena.»
    Un saludo.