| Artículos | 01 ABR 2007

Contraseñas, claves y almacenamiento seguro de secretos

Tags: Histórico
Curso de criptografía para la Web (III)
Gonzalo Alvarez.
En esta entrega se explican varias herramientas criptográficas de gran utilidad en aplicaciones web, como son la posibilidad de derivar claves de cifrado robustas a partir de contraseñas, así como el cifrado de secretos en .Net mediante DPAPI y las nuevas herramientas incluidas en ASP.NET 2.0.

Nivel de dificultad: Bajo
Objetivo: Proteger la información de configuración de aplicaciones web
Herramientas: Las propias herramientas de desarrollo de aplicaciones web

Derivación de claves a partir de contraseñas
Debido a la frecuente confusión entre clave y contraseña, en primer lugar se aclarará su diferencia. Una contraseña es una cadena de caracteres ASCII fácil de introducir y recordar por un usuario, como por ejemplo: gonzalo07, sor567CAN000, 9i=p?A2_w$. Las contraseñas se utilizan normalmente para autenticar a los usuarios. Una clave, en cambio, es una cadena de bits, de longitud variable, típicamente entre 128 y 256 bits para el cifrado simétrico, como por ejemplo la siguiente clave aleatoria expresada en hexadecimal: 5A89D20FC17EB634.
En .NET, cuando se crea una nueva instancia de una clase criptográfica, como por ejemplo, TripleDESCryptoServiceProvider, siempre se genera automáticamente una nueva clave y un vector de inicialización (IV), que se colocan en las propiedades Key e IV respectivamente. En ocasiones tendrá que generar nuevamente claves para el mismo algoritmo. En este caso, puede crear una nueva instancia de una clase que implemente un algoritmo simétrico, y después crear una nueva clave e IV mediante una llamada a los métodos GenerateKey() y GenerateIV().
Ahora bien, en ocasiones se requiere que el usuario introduzca directamente la clave del algoritmo. Pero claro, a un ser humano le resulta difícil recordar una cadena aleatoria de 128 bits de longitud. La solución al problema de la generación de claves seguras y fáciles de recordar por parte del usuario consiste en utilizar una frase o palabra suficientemente larga que posteriormente es convertida en una clave aleatoria por medio de un algoritmo, proceso denominado key-crunching.
Uno de los métodos más utilizados consiste en calcular el hash de la contraseña y utilizarlo como clave. Este método presenta el inconveniente de producir siempre la misma clave para la misma contraseña. Esta desventaja puede contrarrestarse añadiendo un número aleatorio o salt a la contraseña y repitiendo la operación de hash un número arbitrario de veces. En efecto, aunque algo más complejo, tal es el enfoque adoptado por los algoritmos PBKDF1 y PBKDF2 del estándar PKCS#5 v2.0.
En .NET, la clase abstracta DeriveBytes representa cualquier algoritmo que deriva bytes adecuados para su uso como clave a partir de una entrada determinada. Concretamente, la clase PasswordDeriveBytes implanta uno de tales algoritmos, el PBKDF1. Este algoritmo acepta como entrada cuatro parámetros: una contraseña o frase, un valor de salt, el número de iteraciones y un algoritmo de hash. Los valores permitidos para el nombre del algoritmo de hash son “MD5” y “SHA1”. Es muy conveniente utilizar valores de salt generados aleatoriamente, aunque no necesitan guardarse en secreto. En la próxima entrega se hablará de la generación de números aleatorios. Obviamente, al derivar la clave a partir de la contraseña en las operaciones de cifrado y descifrado hay que utilizar el mismo conjunto de parámetros para obtener el mismo resultado.
El siguiente código muestra cómo se puede derivar una clave de 128 bits (16 bytes) a partir de una cadena con la contraseña y de un salt:

Dim password As String = “HarryPotterYElMisterio DelPrincipe”
Dim salt As Byte() = {23,12,132,99,2,123,221,190}
Dim int count As Integer = 20
Dim strNombreHash As String = “MD5”
Dim pdb As New PasswordDeriveBytes( password, salt, strNombreHash, count)
Dim derivedBytes As Byte() = pdb.GetBytes( 16 )

En Java, las clases javax.crypto.spec.PBEKeySpec y javax.crypto.spec.PBEParamSpec se utilizan para configurar el objeto que derivará la clave a partir de la contraseña. Java introduce la particularidad de leer la contraseña desde un array de char en lugar de desde un String, ya que los objetos de tipo String son inmutables y no se pueden sobrescribir sus contenidos tras su uso. Al igual que en .NET, se debe especificar además el valor del salt, el número de repeticiones y el algoritmo de hash, como se muestra en el siguiente ejemplo, donde la función readPasswd(), no mostrada, devuelve la contraseña en un array de char tras obtenerla del usuario:

PBEKeySpec pbeKeySpec;
PBEParameterSpec pbeParamSpec;
SecretKeyFactory keyFac;
byte[] salt = {(byte)0xc7, (byte)0x73, (byte)0x21, (byte)0x8c, (byte)0x7e, (byte)0xc8, (byte)0xee, (byte)0x99};
int count = 20;
pbeParamSpec = new PBEParameterSpec(salt, count);
pbeKeySpec = new PBEKeySpec(readPasswd());
keyFac = SecretKeyFactory.getInstance(“PBEWithMD5AndDES”);
SecretKey pbeKey = keyFac.generateSecret(pbeKeySpec);

Protección de datos en Windows con DPAPI
La API de protección de datos (DPAPI) de Microsoft, incorporada a los sistemas operativos desde Windows 2000, permite cifrar información en una máquina mediante TripleDES sin necesidad de proporcionar explícitamente una clave de cifrado. Consta, nada más (y nada menos) que de dos funciones, una para cifrar y otra para descifrar, llamadas respectivamente CryptProtectData() y CryptUnprotectData(), que proporcionan confidencialidad de datos a nivel de sistema operativo (no llaman a funciones externas). Su finalidad más importante es facilitar un mecanismo seguro y sencillo de usar para proteger datos sensibles en las aplicaciones, como por ejemplo contraseñas, claves de cifrado y cadenas de conexión. Las interfaces públicas de DPAPI se ofrecen a través de crypt32.dll, que forma parte de la CryptoAPI y está incluida en el sistema operativo a partir de Windows 2000.
La forma de operar es muy sencilla: a la rutina CryptProtectData() se le pasa texto claro como un DATA_BLOB y devuelve otro DATA_BLOB con el texto cifrado. Un BLOB es un tipo de datos típico para almacenar grandes objetos binarios en bases de datos (Binary Large OBjects). Este BLOB de datos cifrados es una estructura opaca, que además de los datos contiene información adicional para ayudar a DPAPI a descifrarlo.
Para el cifrado, DPAPI genera aleatoriamente una clave maestra protegida mediante la contraseña del usuario. Se utiliza el estándar PKCS#5 para derivar una clave TripleDES a partir de la contraseña. Con esta clave se cifra la clave maestra, que se guarda finalmente en el almacén personal del usuario. Sin embargo, la clave maestra no se utiliza directamente para cifrar los datos, sino que cada vez que se desea cifrar algo se genera una clave de sesión a partir de la clave maestra, algunos datos aleatorios, y cualquier entropía adicional que proporcione la aplicación. Esta clave de sesión con la que efectivamente se cifran los datos nunca se almacena. Los datos aleatorios se almacenan en el BLOB con el texto cifrado para poder regenerarlos al descifrar.
Utilizar indefinidamente la misma clave de cifrado es una mala de idea de seguridad. Si se viera comprometida, se revelarían automáticamente todos los secretos cifrados con ella. Por este motivo, la clave maestra caduca al cabo de tres meses y se genera una nueva. Ahora bien, ¿cómo pueden descifrarse entonces datos que hayan sido cifrados con ella? La respuesta está en que en el almac

Contenidos recomendados...

Comentar
Para comentar, es necesario iniciar sesión
Se muestran 0 comentarios
X

Uso de cookies

Esta web utiliza cookies técnicas, de personalización y análisis, propias y de terceros, para facilitarle la navegación de forma anónima y analizar estadísticas del uso de la web. Consideramos que si continúa navegando, acepta su uso. Obtener más información