Hablemos de Seguridad - Parte 3: Protocolos

Publicado por Thorsten Prumbs el

SeguridadProtocolosOpenIDOAuth

En esta tercera entrega de nuestra pequeña serie de artículos sobre el mundo de la seguridad vamos hablar de los protocolos. Vamos a suponer que ya te hayas leído la segunda entrega de esta serie sobre Medidas de Seguridad, ya que te facilitará la lectura.

¿Qué son los protocolos?

La RAE conoce muchos significados de protocolo, aquí nos interesan estos:

Secuencia detallada de un proceso de actuación científica, técnica, médica, etc.
Conjunto de reglas que se establecen en el proceso de comunicación entre dos sistemas.

Si aplicamos estos significados al mundo de la seguridad, hablamos sobre todo de protocolos criptográficos y de Protocolos de comunicaciones porque seguridad empieza cuando interactuamos con el mundo exterior (nos comunicamos) y pueden existir adversarios que no se alinean con nuestros intereses, es decir, no podemos confiar en que tengan solo intenciones buenas y correctas.

Todos los conjuntos de reglas de comunicación y de criptografía conforman protocolos de seguridad que intentan mitigar riesgos, forzando comportamiento con medidas de seguridad. En la entrega anterior de esta serie (parte 2) dimos más detalle sobre medidas de seguridad.

¿Cómo se describe un protocolo?

Para describir los protocolos de seguridad se utiliza una notación también conocida como Alice & Bob notation (Security protocol notation).

Para entender mejor esta notación es de ayuda conocer los personajes ficticios utilizados: Alice y Bob. Sin embargo, para demostraciones formales de protocolos se suele utilizar también la BAN-Logic que incluye notaciones para reflejar orígenes de mensajes, su novedad y su fiabilidad.

Algunos conceptos importantes primero

Hablaremos de protocolos de seguridad en el mundo digital y por lo tanto tendremos que hablar de protocolos criptográficos donde hay ciertos conceptos y tecnologías que conviene conocer para no perderse luego en una descripción o configuración técnica de protocolos.

Random y Pseudo-Random

La aleatoridad (randomness) es la falta de patrones o de predictibilidad de eventos. A nivel de comunicación una señal aletatoria se considera ruido (noise).
La propiedad de aleatoridad es importante para la encriptación, es decir, la protección o ocultación de datos ante terceros.

Una perfecta encriptación de datos se debe parecer al máximo a ruido y resulta que no es nada fácil esconder datos con sentido en ruido. En la mayoría de mecanismos de encriptación se necesitan generadores de números aleatorios, es decir, números prácticamente imposible de predecir.
Muchos generadores de números aleatorios utilizan fuentes naturales de cambios para las cuales todavía no existe un modelo adecuado de predicción, p.ej. calor, movimiento humano del ratón, etc.

No es nada fácil crear una fuente de números verdaderamente aleatorios. En algunos casos se considera suficiente una fuente reproducible de números aleatorios. Es más fácil de implementar de forma determinística y facilita las pruebas. Dichas fuentes de números aleatorios reproducible se llaman números pseudo-random.

En el mundo Java tenemos una clase para la generación de números aleatorios pseudo-random llamado java.util.Random. Para usos criptográficos se recomienda el uso de la clase java.security.SecureRandom que al final delega la generación a una fuente de entropía que varia según la implementación. Ver JavaDoc de la clase SecureRandom:

Note: Depending on the implementation, the generateSeed and nextBytes methods may block as entropy is being gathered, for example, if they need to read from /dev/random on various unix-like operating systems.

Nonce

En el contexto criptográfico, un nonce es un número aleatorio que sólo se puede o debe utilizar una única vez. Se suele entender como abreviatura de number used once. Para garantizar el único uso se suelen utilizar time stamps o números aleatorios con una probabilidad insignificante de repetición. Ver Cryptographic nonce.

Un nonce suele ser un número random o pseudo-random y se utiliza muchas veces en protocolos de autenticación para impedir los ataques de replay.

UUID

El universally unique identifier (UUID) es un identificador único universal o global (GUID). Se utiliza para aquellos casos donde se necesita un identificador único pero no se puede depender de una instancia central de coordinación. A pesar de que la probabilidad de duplicados no es cero, su probabilidad es despreciable.

Existen varios estándares de formatos y generación de UUID tanto de la ISO como de la IEFT, entre otros (ver UUID Standards.

Se pueden distinguir distintas composiciones o versiones:

  • UUID Nulo (00000000-0000-0000-0000-000000000000)
  • UUID Versión 1 (fecha-hora y la dirección MAC)
  • UUID Versión 2 (fecha-hora y la dirección MAC, DCE versión de seguridad)
  • UUID Versiones 3 y 5 (hashed namespace name-based)
  • UUID Versión 4 (random)

Cada versión de UUID tiene su campo de aplicación y su forma estándar de generación. Una buena descripción se da en el articulo Guide to UUID in Java de Baeldung.

Hash

De las funciones Hash ya hemos hablado en la segunda parte de esta serie sobre medidas de seguridad aunque desde su uso como autenticación pasiva, integridad del dato o  no repudio.

Otras aplicaciones de funciones Hash son las de generación de números pseudo-random, ver también Cryptographic hash functions.

Las funciones Hash son importantes en protocolos de seguridad cuando queremos transferir claves o secretos sin transportarlos. Es decir, no se comparan secretos, sino se comparan dos hashes. Por ejemplo el hash de una clave introducida por el usuario se compara con el hash almacenado de la clave.

Salt

En el contexto de criptografía, un Salt o sal en español son bits aleatorios utilizados como dato de entrada adicional a funciones Hash con el objetivo de mitigar ataques de diccionario (Ver también Dictionary attack).

Es decir, cuando queremos utiliza una función Hash para no tener que almacenar y comparar un secreto en texto claro, debemos utilizar la función Hash siempre con sal (salted hash).

Modos de Block Cipher

Con los modos de operación de una unidad de cifrado por bloques entramos en los mecanismos más centrales de la encriptación simétrica.
Una unidad de cifrado realiza operaciones de cifrado utilizando un texto claro y una clave. Para el descifrado se utiliza el texto encriptado y la clave para obtener el texto claro. Se llama por bloques porque el algoritmo se aplica sobre grupos de bit de longitud fija - los bloques. Ver Block cipher para entrar en detalle de los distintos diseños de cifrados de bloques.

Los cifrados de bloques tienen varios modos de operación comunes como Electronic Codebook (ECB), Cipher Block Chaining (CBC), Propagating Cipher Block Chaining (PCBC), Cipher Feedback (CFB) Output Feedback (OFB) y Counter (CTR). Todos los modos de operación tienen características muy específicas indicados para distintos casos de uso como por ejemplo:

  • Encryption parallelizable
  • Decryption parallelizable
  • Random read access

Para más detalle ver Block cipher mode of operation.

Con estos modos de operación nos volvemos a encontrar en el mundo Java cuando queremos configurar un algoritmo de encriptación simétrica como AES o DES y utilizamos la clase javax.crypto.Cipher.

Un ejemplo de protocolo: Autenticación

Como ejemplo vamos a analizar un poco las distintas formas de autenticar a un actor (Ver más detalle de autenticación en la anterior entrega) de esta serie).

Es decir, el objetivo es autenticar la identificación presentada por un actor. La manera más simple sería la autenticación por contraseña. Es decir, el servidor S pide al cliente C que le mande una contraseña y solo si la contraseña presentada es correcta, el servidor deja entrar o acceder al cliente.

Aplicado al mundo digital, tendremos que mitigar varios vectores de ataques, impidiendo distintos mecanismos de suplantación de identidad. Es decir, hay que suponer que la comunicación no es segura, es decir, con adversarios que pueden escuchar (eavesdrop) o incluso manipular la comunicación.
Esto es justo el caso de autenticación por HTTP con autenticación básica o por formulario. Hay que confiar en el canal de comunicación o la autenticación no sirve para confiar en la identificación.
En estos casos estamos acostumbrados a poner HTTPS pero con esto no cambiamos el protocolo de autenticación sino solo movemos el dominio de confianza del canal de comunicación a la gestión de certificados PKI.
Otra manera de securizar la autenticación es la de introducir mecanismos de criptografía.

Para ver más ejemplos de protocolos de autenticación que se documentan con mucho más detalle, os recomiendo el libro de Security Engineering de Ross Anderson, disponible de forma gratuita.

Autenticación desafío-respuesta con mecanismos de encriptación

Con la ayuda de mecanismos de la encriptación se pueden desarrollar protocolos de autenticación que mitigan diversos tipos de ataque.

Para mitigar el problema del canal de comunicación inseguro, se suelen emplear mecanismos de compropación si tanto el cliente como el servidor conozcan un secreto pero sin transmitir el secreto en texto claro.
Una manera de conseguir esta comprobación es utilizar el secreto como clave de encriptación de alguna información aleatoria que manda el servidor como desafío y que el cliente tiene que poder desencriptar para poder aplicar una función numérica determinada, volver a enriptarlo y mandarlo como respuesta. En el caso del protocolo Kerberos el desafío es un número entero N encriptado al cual el cliente tiene que sumar uno (N + 1).
Aunque este mecanismo no transmite el secreto, un atacante podría utilizar ataques de diccionario o de fuerza bruta para obtener el secreto.

Otro posible ataque es el de un ataque de replay donde el atacante captura una respuesta del cliente a un desafío, pide al servidor su propio desafío y responde con la respuesta anteriormente capturado.
Este ataque se mitiga utilizando desafíos con información aleatoria.

Normalmente, los protocolos de autenticación utilizan Nonces para impedir un ataque de intermediario haciendo de cada secuencia desafío-respuesta una secuencia única.

Además se suele mitigar la posibilidad de un servidor dudoso que re-implementa al servidor real, autenticando el servidor y el cliente mutuamente. Es decir, el servidor se asegura que el cliente conoce el secreto y el cliente se asegura que el servidor conoce el secreto. Ver también Mutual authentication.

Por ejemplo, una secuencia simple de desafío-respuesta consiste en:

  • El servidor envía un desafío único ds al cliente.
  • El cliente envía un desafío único dc al servidor.
  • El servidor calcula su respuesta con rs = hash(dc + secret) y lo envía al cliente.
  • El cliente calcula su respuesta con rc = hash(ds + secret) y lo envía al servidor.
  • El servidor calcula el valor esperado de rc y lo compara con la respuesta del cliente.
  • El cliente calcula el valor esperado de rs y lo compara con la respuesta del servidor.

donde

  • ds es el desafío generado por el servidor.
  • dc es el desafío generado por el cliente.
  • rc es la respuesta del cliente.
  • rs es la respuesta del servidor.

Fuente: Wikipedia - Challenge–response authentication.

Muchas implementaciones de protocolos desafío-respuesta nos sonarán (Ver también Authentication protocol):

¿Qué pasa con OpenID y OAuth?

En primer lugar hay que diferenciar entre protocolos y frameworks o APIs. Un protocolo describe qué y cómo a un nivel suficientemente abstracto para no depender o no mezclar con implementaciones o restricciones técnicas de una tecnología concreta. Frameworks y APIs describen protocolos "implementándolos".

Ahora que tenemos una pequeña impresión de lo que es un protocolo de autenticación, podemos hablar de OpenID y OAuth y por qué no se consideran protocolos de autenticación.

OpenID

OpenID es un protocolo de autenticación decentralizado. Es decir, es un protocolo por encima, separando las dos partes de una autenticación: quién quiere autenticar a un usuario (Relying party) pero sólo necesita la identificación y quién autentica al usuario (Identity Provider). El "meta-protocolo" OpenID no especifica de qué manera un Identity Provider autentique a un usuario.
Por lo tanto, la seguridad de OpenID depende de la confianza del usuario en el Identity Provider. El Relying party queda fuera porque está interesado exclusivamente en la identificación del usuario.

Es en el lado del Identity Provider dónde se emplean los distintos mecanismos y protocolos de autenticación anteriormente explicados.

OAuth

OAuth es un protocolo de autorización decentralizado. Es decir, es de nuevo un protocolo por encima, separando el propietario del recurso (resource owner) del servidor de autorización (authorization server) y del interesado (third party). En un control de acceso con OAuth, el sistema que protege la información de un usuario (p.ej. Facebook, Twitter, Google, etc.) es el Resource Owner. El cliente quien quiere acceder a dicha información es el third party (p.ej. Disqus, koliseo, etc.). Un authorization server (p.ej. Google, Facebook, etc. porque no sólo tienen los datos personales sino también la identidad) emite un Token de acceso al third party y éste accede con este token al recurso del Resource Owner.

Dicho token de acceso contiene información de permisos de acceso junto con información de contexto. OAuth se limita a delegar el acceso con grants. Este mecanismo simple se puede integrar con XACML, otra forma mucho más granular de dar acceso basado en atributos que se utiliza sobre todo para implementar políticas de acceso (access policies).

Ahora bien, OAuth no define ningún mecanismo de autenticación, solo de autorización. Como la autorización conlleva una identificación, el mecanismo de dar acceso o no, se puede utilizar como mecanismo de autenticación implícita. En este caso se puede hablar de una pseudo-autenticación con OAuth (OpenID vs. pseudo-authentication using OAuth).

Adicionalmente OAuth no utiliza mecanismos de encriptación para protegerse contra ataques sino depende enteramente de la seguridad a nivel transporte TLS.

La diferencia entre un protocolo interoperable y un framework se ha manifestado en el caso de OAuth cuando uno de sus iniciadores Eran Hammer salió definitivamente del grupo de definición con cierta polémica.
Recomiendo leer sus razones en OAuth 2.0 and the Road to Hell.

Protocolos de seguridad en el mundo real

A lo largo de este articulo hemos conocido algunos protocolos de seguridad que, en la mayoría de los casos, incluyen mecanismos de criptografía. Estos mecanismos de criptografía se basan en modelos matemáticos y suelen llevar configuraciones específicas de algoritmos de Hash, tipos de block cipher y longitud de claves.
Con el constante crecimiento de la capacidad de computación (Ley de Moore), la capacidad de los distintos adversarios sigue creciendo y las configuraciones de los protocolos se tienen que adaptar para seguir suponiendo una defensa a los distintos tipos de ataques.

Existen varias fuentes con recomendaciones de configuración de nuestros sistemas de seguridad digital:

Aunque se puede llegar a certificar la eficacia de un protocolo contra ciertos vectores de ataques, esto no es ninguna garantía de que todas sus implementaciones también lo sean. Esto se ha podido observar con el famoso KRACK Attacks del protocolo Wi-Fi Protected Access.

Conclusiones

Terminamos con una frase de Ross Anderson que nos prepara ya un poco para la siguiente entrega de esta serie.

It is wrong to assume blindly that security protocols exist to keep ‘bad’ guys ‘out’.
They are increasingly used to constrain the lawful owner of the equipment in which they are built;
their purpose may be of questionable legality or contrary to public policy.

Ver: Security Engineering by Ross Anderson. Chapter 3: Protocols

En la siguiente entrega hablaremos del estado del mundo del software para saber de dónde partimos si queremos construir un mundo digital un poco más seguro.

Para estar al día de la próxima entrega, ¡síguenos en Twitter!

Autor

Thorsten Prumbs

Arquitecto Empresarial de la Dirección de Tecnología de knowmad mood.
Especialista en ALM, DevSecOps, Java EE, rendimiento y seguridad. Siempre aprendiendo por ingeniería inversa.