lunes, 31 de octubre de 2016

Échales sal a tus contraseñas

Guardar las contraseñas de los usuarios de forma segura es siempre fuente de dolores de cabeza.

Querido administrador: ¿Sabes cómo estás almacenando las claves en tu sitio web? ¿Tienes idea del desastre que puede significar que te las roben?

El problema

En cualquier sistema donde haya que dar servicio a usuarios concretos (sistema operativo, base de datos, aplicación web, etc.), normalmente se usan contraseñas para identificarlos. El sistema tiene que almacenar necesariamente en algún soporte las contraseñas para poder verificarlas cada vez que un usuario se autentica. La solución simple es guardar un archivo o una tabla con las contraseñas (en texto claro). Aparentemente esto debería ser seguro, siempre que dicho soporte esté debidamente protegido de accesos no autorizados. Pero en realidad es muy mala idea.
El método de almacenar las contraseñas en claro, que sorprendentemente se sigue usando hoy día muy a menudo, se demuestra inseguro por la simple razón de que proteger un archivo eternamente es imposible. Nunca podemos suponer a priori que el almacén de claves no va a ser comprometido. Lo peor de todo es que  un atacante que accediera a las contraseñas en claro, aunque sea por un breve lapso, compromente automáticamente a todos los usuarios y podría suplantar inadvertidamente a cualquiera de ellos en cualquier momento. Si combinamos esto con el hecho de que muchos usuarios usan las mismas contraseñas en diferentes sitios, las consecuencias son catastróficas.
En 2009, el sitio de juegos sociales rockyou.com sufrió un ataque que reveló las contraseñas en claro de 32 millones de usuarios. Aún hoy, en muchos sitios se ofrece la posibilidad de recuperar por email (canal inseguro donde los haya) la misma contraseña que ya usábamos, demostrando que se almacena en claro.

Encriptar las contraseñas

Se puede abordar el problema de la protección del almacén de contraseñas aplicandoles algún algoritmo de encriptación, como los cifrados simétricos. Sin embargo, tampoco es buena idea, porque:
  • El atacante conocería la longitud de cada una de las contraseñas.
  • El atacante podría saber qué usuarios comparten la misma contraseña, lo que va a facilitar enormemente averiguarla.
  • Si se viera comprometida la clave de cifrado, se revelarían automáticamente todas las claves y estaríamos en el mismo problema del almacén en claro.

Uso de las funciones de hash para almacenar contraseñas

Una idea clave para abordar el problema del almacén de contraseñas es darse cuenta de que no es necesario desencriptarlas. Cada vez que el usuario accede podemos volver a encriptar y comparar los valores encriptados. En la práctica no es necesario tener una clave de cifrado, ni siquiera un cifrado propiamente dicho, sino que podemos almacenar valores de hash de cada clave. Las propiedades de las funciones de hash son ideales para este propósito:
  • Todas las contraseñas producen un hash del mismo tamaño, por lo que el atacante no puede conocer la longitud de cada clave.
  • No hay una clave de cifrado, que pueda revelar automáticamente todas las contraseñas.
  • Por definición es muy difícil revertir una función de hash. El atacante necesitaría un enorme esfuerzo computacional para sacar partido del almacén.
El uso del hash para almacenar claves es, por tanto, un salto enorme en seguridad respecto a los anteriores métodos. En las primeras versiones de Unix se utilizaba un enfoque similar, y era tal la confianza en este método, que el almacén de claves era nada menos que un archivo de acceso público (el conocido /etc/passwd). Sin embargo, un análisis más profundo revela dos defectos fundamentales:
  • El atacante sigue sabiendo qué usuarios comparten contraseñas, puesto que los valores de hash son idénticos para entradas idénticas.
  • El almacén es vulnerable a los ataques de diccionario, es decir, el atacante puede usar la misma función de hash para probar coincidencias con palabras comunes y sus combinaciones.

Echandole "sal" al hash

Los dos inconvenientes anteriores se pueden mitigar añadiendo al sistema un valor aleatorio que se asocia a cada contraseña. A este valor se le suele llamar sal (del inglés salt), y al método resultante hash salado (salted hash). Lo que vamos a hacer es elegir una sal diferente para cada usuario y almacenarla junto al resultado de aplicar la función de hash a la contraseña concatenada con la propia sal. Es decir:
  1. Elegimos una sal para la contraseña x.
  2. Aplicamos la función de hash tal que h=H(x||s)
  3. Almacenamos los valores s y h (ya sea juntos o por separado).
El valor de la sal no tiene que ser secreto necesariamente, aunque puede mantenerse aparte en un archivo protegido para añadir un nivel más de seguridad. En versiones modernas de Unix (y sus derivados) es común guardar la sal y el hash en un archivo protegido llamado /etc/shadow.
La consecuencia más directa de este mecanismo es que dos contraseñas iguales no producen hashes iguales, evitando el problema de las contraseñas compartidas. El problema de los ataques de diccionario no se soluciona por completo, dado que atacar una contraseña sigue costando lo mismo (a no ser que guardemos la sal en secreto). Sin embargo, los ataques masivos a todo el almacén de claves se hacen mucho más costosos computacionalmente, dado que el atacante no va a poder usar tablas precalculadas de los hashes (llamadas rainbow tables). Por ejemplo, para hacer una tabla usando una sal de 32 bits tendríamos que calcular y almacenar unos 4.300 millones de hashes (232) por cada entrada del diccionario, en lugar de uno sólo si no hubiera sal.

Cadenas de hashes

El método del hash con sal es el mejor que conocemos para el problema de almacenar contraseñas en el sentido clásico, es decir, que se han de usar múltiples veces para verificar al usuario (por ejemplo cada vez que entra en el sistema). Para sistemas que requieran un nivel de seguridad mayor, se pueden usar también las propiedades del hash para generar contraseñas de un solo uso (lo que se conoce como OTPone-time password).
Esto se hace eligiendo un valor aleatorio inicial y encadenando la función de hash tantas veces como contraseñas queramos generar. Luego daremos al usuario la lista de los hashes generados (h1, h2, ..., hn) y almacenaremos localmente sólo el último valor hn+1=H(H(H(H(...(x)...))). A partir de ahí, cada vez que el usuario se autentique, le vamos pidiendo sucesivamente las contraseñas en orden inverso y actualizando el valor del hash almacenado. Por ejemplo, si tenemos almacenado el valor h100, hacemos lo siguiente:
  1. El usuario nos proporciona h99 como clave.
  2. Calculamos h100=H(h99y vemos que coincide con el almacenado.
  3. Guardamos h99 como nuevo valor almacenado.
La siguiente vez que se autentique, el usuario tendrá que proporcionar h98 para entrar. Nunca se usa dos veces la misma contraseña.
Las ventajas son importantes:
  • No hay secreto almacenado. Un ataque de diccionario tendría poco sentido y el almacén incluso se puede hacer público.
  • Hay menor riesgo de que el usuario revele inadvertidamente su clave al teclearla, o sufra un ataque de key-logger (monitorización del teclado).
  • El usuario no puede elegir las contraseñas, por lo que son más aleatorias.
El inconveniente claro es que es un mecanismo molesto para el usuario, que tendrá que llevar consigo una lista de claves. Esto se puede mitigar un poco usando un programa de utilidad que guarde esa lista y vaya proporcionando las claves a medida que se necesitan, o bien utilizando algún tipo de soporte para la clave (por ejemplo una tarjeta inteligente).

viernes, 28 de octubre de 2016

TCP/IP (1): ¿Qué es TCP/IP?

Vinton Cerf, uno de los inventores de TCP/IP.
Entradas de la serie -> TCP/IP

TPC/IP es una familia de protocolos que se usan como estándar en Internet. Los más conocidos son TCPIPUDPFTP, etc. Por ser los dos primeros los más generales, dan nombre a toda la familia.
Existe otra versión de TCP/IP, que se llama MILSPEC, definida por el Departamento de Defensa de EE.UU., y que es más formalizada que la original.
PROTOCOLOS BÁSICOS: Los protocolos de bajo nivel son tres: IPTCP UDP. Se encargan de dar servicio al resto de protocolos especializados de alto nivel.
PROTOCOLOS DE APLICACIÓN: Se especializan en tareas determinadas, dejando a los de bajo nivel el establecimiento y control de las conexiones. Algunos de ellos son:
  • FTP  (Transferencia de ficheros)
  • RLP (Impresión remota)
  • TELNET (Ejecución remota)
  • HTTP (Transferencia de hipertexto)
  • etc.
ESTRUCTURA EN CAPAS
TCP/IP se estructura en capas, ocupando aproximadamente los niveles Red y Transporte del modelo OSI. Cada uno de ellos es como una librería de rutinas a ser llamadas para gestionar las comunicaciones.
IP es una librería de rutinas que pueden ser llamadas por TCP o por otros protocolos y aplicaciones directamente. Se dedican a tareas básicas de entrega de paquetes, abstrayendo la red física subyacente (LAN, línea serie, etc.). Se encarga de encontrar la ruta hacia su destino, pero no se preocupa de que los paquetes se pierdan o queden desordenados. A esto se la llama entrega no confiable. Estas son las rutinas que todo programa necesitará, ya sea por sí mismo o a través de TCP.
Es también una entrega punto a punto, pues se abstraen las redes intermedias por las que pasan los datos, dando la impresión a la capa superior de que existe una conexión directa entre los extremos.
TCP es responsable de hacer que los comandos y mensajes lleguen a su destino correctamente, reordenándolos y retransmitiendo lo que se pierda por el camino. Es lo que se llama transporte confiable. Además, TCP se encarga de separar los datos en DATAGRAMAS (unidades en que se divide el flujo de información para hacerla más manejable). TCP funciona también como librería de rutinas que son llamadas por las aplicaciones.
La ventaja de la separación en capas conceptuales es que TCP/IP se convierte así en una arquitectura robusta y adaptable. Es posible reemplazar o mejorar un servicio sin afectar a los demás. En la práctica, la arquitectura en capas no es tan directa como aparece en la figura, sino que el software IP puede comunicarse con varios módulos a nivel de transporte y/o con varios interfaces físicos.
En las máquinas intermedias (gateways) que dan paso a los datagramas a través de las redes por donde van pasando, éstos sólo llegan a subir hasta la capa IP, pues es ésta la que se encarga de su rutado. Para los niveles superiores al IP, los gateways simplemente no existen, de la misma manera que IP no tiene conocimiento de los puentes (nivel de enlace) y los repetidores (nivel físico) por los que pasan los datagramas.
Ha de tenerse en cuenta que la equivalencia entre capas OSI y capas TCP/IP no es perfecta, pues los presupuestos subyacentes son diferentes, y ambos modelos han tenido origen en trabajos muy distintos.
La principal diferencia está en la confiabilidad de la capa de red. En el modelo OSI el nivel de red contiene comprobación de errores y retransmisión, mientras que el protocolo IP se basa en la idea de que la confiabilidad punto a punto es más un problema que una ventaja, dejando a la capa de transporte manejar la mayor parte de la recuperación de errores.
La principal consecuencia de este planteamiento es que las máquinas finales intervienen activamente en la detección y corrección de errores, mientras que en el modelo OSI es el proveedor el que se encarga de ésto. Se puede ver, por tanto, a una red TCP/IP como un conjunto de nodos inteligentes unidos por un servicio de entrega muy sencillo.
Equivalencia con modelo OSI

EL MODELO CATENET
TCP/IP sigue el modelo Catenet, consistente en la interconexión de redes locales mediante gateways (también llamados routers). Un usuario será capaz de acceder a cualquier máquina de cualquiera de los sistemas interconectados (es la base de la idea de Internet como "red de redes"). Los datagramas pueden pasar por decenas de redes antes de llegar a su destino, siendo esto totalmente transparente al usuario. 
Se sigue un tipo de protocolo sin conexiones, es decir, cada datagrama se trata por separado, sin establecerse una conexión permanente por la que fluyan los datagramas. Para la red, cada datagrama no tiene relación con ningún otro. En este sentido, el término "datagrama" en la jerga de TCP/IP no coincide con el concepto tradicional que se le suele atribuir.

LA DIRECCION IP
La dirección que identifica a cada nodo en el nivel IP se compone de cuatro números de 8 bits cada uno. Está pensado para que sirva de guía de la localización de la máquina desde entornos más grandes (los primeros números), discriminando con los últimos. Los números 0 y 255 no se usan pues tienen significados especiales.
La notación habitual es en formato decimal separando los números con puntos, p.e. 192.168.1.101
Una dirección IP no especifica necesariamente una computadora individual, sino una conexión de red. Así pues, un solo nodo puede tener varias direcciones, según el número de redes a las que esté conectado. Cuando un ordenador tiene varias interfaces de red sin ser un gateway, se le llama multi-homed.
Un inconveniente de este sistema de direccionamiento según conexión es que cuando un ordenador se mueve de una red a otra, debe cambiar su dirección. Así pues, las computadoras personales o las portátiles a menudo no pueden tener asignada una dirección permanentemente.
La cantidad de direcciones IP posibles (en la versión 4, que es la más extendida) parecía en su momento que era inalcanzable, pero con la expansión que ha tenido Internet a nivel mundial, se ha quedado pequeño. En la siguiente versión del protocolo (versión 6), este espacio se amplia enormemente, pero su uso todavía no está muy extendido. Parece claro que el agotamiento de direcciones, tarde o temprano, forzará el cambio de versión en todo el mundo.