Sie sind auf Seite 1von 11

12 Almacenando contraseñas

Como ya dijimos en la introducción, debemos tratar de


distribuir la seguridad de nuestra aplicación en varias capas y
no como una única “pared exterior” que mantenga a los
atacantes afuera. En este capítulo hablaremos sobre qué
acontece si un atacante, interno o externo, consigue acceso a
nuestra base de datos y obtiene una lista de los usuarios y sus
contraseñas.

Una gran mayoría de los usuarios usa la misma contraseña para


todos, o casi todos, los sitios a los que accede. La consecuencia
de este hecho es que si alguien tiene la contraseña de un usuario
y su dirección de correo electrónico es probable que pueda
acceder a su casilla de correo y a los otros sitios donde ese
usuario tenga una cuenta creada.

Veremos cómo agregar una capa más de seguridad a nuestra


aplicación para que si alguien puede obtener una lista con la
información de los usuarios, no pueda saber cuál es la
contraseña real y usarla para robarles la identidad.

Criptografía
La criptografía es la ciencia relacionada con el ocultamiento de
la información. Existe prácticamente desde la invención de la
escritura. En sus comienzos se usaban métodos simples de
substitución, había una tabla de conversión y para descifrar el
mensaje oculto se reemplazaban los caracteres de un texto por

97
los de la tabla y se obtenía el mensaje original. Se denomina
criptograma al mensaje cifrado.

Como ya mencionamos, una de las técnicas más antiguas (y


básicas) de encriptación son las que, dado un mensaje, se
substituyen ciertos caracteres por otros. Por ejemplo,
reemplazando un caracter por el que le sigue X posiciones
adelante o detrás en el alfabeto. Este método, usando la
substitución de una letra por la que le sigue tres posiciones
adelante es llamado cifado de César por haber sido usado por
Julio César para sus comunicaciones secretas. Demás está decir
que este tipo de encriptación puede haber sido seguro en algún
momento de la historia pero actualmente no pasa de un juego
de niños. Veámoslo en la práctica: si substituimos cada caracter
del mensaje esta noche Mirtha, te invito a morfar por el caracter
que se encuentra tres posiciones después de él obtenemos hvwd
qrfh Pluwkd, wh lqylwr d pruidu. Existe una variación del
método, llamada ROT-13, donde se reemplaza un caracter por
el que está 13 posiciones a la derecha. Es usado principalmente
en foros (y anteriormente en Usenet) cuando se está hablando
sobre algún libro, película, etcétera y se detalla el fnal o cierta
parte de la trama que puede arruinar la experiencia a la gente
que todavía no la vio/leyó. Estos son los llamados spoilers.

Usualmente se le aplica un método criptográfco a un texto


para obtener un mensaje cifrado y para obtener el mensaje
original debe aplicársele nuevamente. Estos métodos necesitan
de una clave. Si se usa la misma clave tanto para encriptar como
para desencriptar, son llamados de clave simétrica. Si por el
contrario, se debe utilizar una clave para encriptar y otra para
desencriptar, los métodos son llamados de clave asimétrica.

98 Almacenando contraseñas
Uno de los usos de los métodos de clave simétrica es la
construcción de funciones de hash. Estas funciones toman como
entrada un mensaje y su salida, usualmente llamada digest, es
una cadena de largo fjo que varía según la función. Estas
funciones están diseñadas de tal manera que no es posible
obtener el mensaje original a partir del digest. Y además es muy
importante que no tengan colisiones. Existe una colisión
cuando para dos mensajes diferentes el resultado es el mismo
digest. Algunos ejemplos de funciones de hash conocidas son
SHA-1, SHA-2 y MD5. SHA-2 es una familia de funciones
cuyos nombres indican la longitud en bits de su salida: SHA-
224, SHA-256, SHA-384 y SHA-512.

Veamos un ejemplo: como dijimos, si usamos alguna de estas


funciones, no importa el largo del mensaje de entrada, la
longitud del digest generado es siempre la misma. Usaremos
SHA-1. Vemos que dos cadenas diferentes, una de 25
caracteres de largo y la otra de un caracter producen ambas un
digest de 160 bits (20 caracteres de un byte cada uno).

$ irb -rdigest/sha1
ruby > Digest::SHA1.hexdigest('Ministro de
Ahorro Postal')
=> "d1ec5299ce555b9c0e2bdf0c7cdf7e5cc1850737"
ruby > Digest::SHA1.hexdigest('a')
=> "86f7e437faa5a7fce15d1ddcb9eaeaea377667b8"

Uno de los principales usos que se les da a estas funciones es


obtener una frma para un mensaje. Teniendo el mensaje y la
frma podemos verifcar que el mensaje no fue modifcado. Este
mensaje puede ser desde un bloque de texto hasta un archivo
binario con código fuente o una imagen ISO.

Almacenando contraseñas 99
Casos prácticos
Vamos a analizar diferentes opciones de cómo almacenar las
contraseñas teniendo en cuenta lo que ahora sabemos de
criptografía. Vamos a empezar desde la opción más sencilla de
implementar y la más vulnerable hasta llegar a las más
complejas y efectivas.

Texto plano
Esta es sin duda la opción más naíf. Consiste en almacenar la
contraseña del usuario tal cual como fue ingresada por él. Esta
opción es usada generalmente porque no requiere ningún
esfuerzo extra. También se cree que dado que la información
queda almacenada en la base de datos de la aplicación y sólo
puede ser accedida por los desarrolladores o el DBA, la
información está sufcientemente segura. Quienes piensan así
no tienen en cuenta que un atacante puede ser interno y que
cualquiera que tenga acceso a la base de datos (incluyendo al
DBA o los desarrolladores) podrá ver esas contraseñas. Además,
si algún atacante externo logra el acceso a la base de datos, al
tener la información de los usuarios es probable que pueda
acceder a todas las cuentas de los mismos. No olvidemos que
muchas personas usan la misma contraseña para casi (o todas)
las cuentas que tienen en diferentes sitios, así, saber la clave de
un usuario y su dirección de correo electrónico es casi como
tener acceso a todo el contenido e información que tengan en
otros sitios.

Después de estas consideraciones es interesante analizar los


sitios que habitualmente visitamos y donde tenemos un usuario
creado. ¿Tienen una opción para recordarnos la contraseña en

100 Almacenando contraseñas


caso de haberla olvidado? Pruébenlo y vean si en el mail nos
envían nuestra contraseña. Si es así, la están almacenando en
texto plano.

Cifrado
Si ya nos convencimos de que almacenar las claves en texto
plano es una muy mala idea, podemos pensar que con poco
esfuerzo conseguiremos una versión encriptada (y casera) de las
claves. Decidimos entonces hacer un cifrado de las contraseñas
y para ello reemplazamos cada caracter de la clave por el que le
sigue 42 posiciones más adelante. Aplicamos la substitución no
sólo a las letras del abecedario (de la a a la z) sino a todos los
caracteres posibles. El siguiente ejemplo lo haremos en Java.
Creamos una clase Cipher que se encargará de
encriptar/desencriptar los mensajes:
public class Cipher {

private enum Operation {


PLUS, MINUS
};

private int x;

public Cipher(int x) {
this.x = x;
}

public String encrypt(String s) {


return transform(s, Cipher.Operation.PLUS);
}

public String decrypt(String s) {


return transform(s,
Cipher.Operation.MINUS);

Almacenando contraseñas 101


}

protected String transform(String s,


Operation op) {
StringBuilder result = new
StringBuilder(s.length());
for (char c : s.toCharArray()) {
if (op == Operation.PLUS)
c += x;
else
c -= x;
//suponemos un conjunto de 256 caracteres
//de 1 byte. Este método no funciona con
//caracteres compuestos por más de un
byte.
result.append((char) (c % 256));
}
return result.toString();
}
}

Y hacemos un test unitario con JUnit:


import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

public class CipherTest {

private Cipher cipher;

@Before
public void setUp() {
cipher = new Cipher(42);
}

@Test
public void encrypt() {
final String x = "ABC";

102 Almacenando contraseñas


assertEquals("klm", cipher.encrypt(x));
}

@Test
public void decrypt() {
final String x = "Saber demasiado nunca
simplifica las decisiones";

assertEquals(x,
cipher.decrypt(cipher.encrypt(x)));
}
}

Con el primer test, encrypt, nos aseguramos de que el


mensaje sea encriptado correctamente y con el test decrypt,
verifcamos que desde la versión encriptada del mensaje se
puede volver al original.

Ahora que tenemos una manera de encriptar las contraseñas y


nos sentimos seguros. Pero veamos lo simple que es encontrar
el mensaje original. El criptoanálisis se dedica a estudiar cómo
obtener el mensaje original a partir de un criptograma, sin
saber la clave o el método de cifrado usado. Un posible ataque a
los métodos de substitución (el que usamos en la clase Cipher)
es el análisis de fecuencia. En cada idioma ciertas letras, o
combinaciones de ellas, ocurren más frecuentemente que otras.
Si analizamos el criptograma y hacemos un conteo de
frecuencia de los símbolos usados podemos compararlo con las
frecuencias de aparición de cada letra o combinación de ellas
en varios idiomas para tener algunas pistas de cómo reemplazar
los caracteres.

Olvidémonos por un momento de la lista de contraseñas y


vayamos a un caso más general, donde tenemos un criptograma

Almacenando contraseñas 103


cuyo tamaño es más grande la longitud usual de una
contraseña. Empezamos suponiendo que tal vez se haya usado
un método de substitución. Sabemos que el lenguaje es
castellano y el criptograma que tenemos es:

CBB7C0B7BEBFC1BFB7C0C6C1B7C0C3C7B7BEBEB7B9B3C4C
1C0B3BEB3C6B7C4C4B3CCB3BEB3BFBBC4B3B6B3C5B7BEB7
C5C2B7C4B6BBC1B6B7C2C4C1C0C6C1BFB3C5B3BEBEB3B6B
7BEC2B3BEBFB7C4B3BEB7C0B7BEBAC1C4BBCCC1C0C6B7BB
C0BFB7C0C5C1B3BCB3C0BBC0B7BEB7C2B3C4B7B5BBC1C3C
7B7B7BEB5BBB7BEC1B7C0C6B7C4C1C4B7C5C1C0B3B4B3B7
C0C7C0B3C0C1C6B3B8C4B3B9C1C4C1C5B3CBB4C4B7C8B7

En el criptograma de arriba, y en el análisis siguiente, usaremos


la representación en hexadecimal de cada byte.

Como ya mencionamos, existen para cada idioma, tablas de


frecuencia de aparición para las letras del alfabeto. En
castellano la tabla de frecuencias es46:

Letra(s) Frecuencia
EA 13%
O 9%
S 8%
RNI 7%
DL 5%
CTU 4%
MP 3%

46 Classical cryptography course, Randy Nichols. Lecture 5, Xenocrypt


morphology http://www.threaded.com/cryptography5.htm

104 Almacenando contraseñas


GYB 1%

El conteo de frecuencias del criptograma lo vamos a hacer con


un pequeño script en Ruby:

#encoded tiene una cadena con el criptograma


#se muestran las frecuencias de simbolos para
#longitud 1 y 2

(1..2).each do |size|
puts "frequency (length=#{size})"
freqs = Hash.new { |h,k| h[k] = 0 }
encoded.chars.to_a.each_cons(size) do |x|
freqs[x.join] += 1
end
freqs.sort_by { |k, v| v }.reverse.each do |
k, v|
p [k, v] if v > 2
end
end

Este es el resultado del script. Para abreviar, veremos sólo las


combinaciones con mayor frecuencia:

Símbolo
Frecuencia
(longitud = 1)
B7 29
B3 22
C0 16
C1 16
BE 15
C4 14

Almacenando contraseñas 105


Símbolo
Frecuencia
(longitud = 1)
BB 7
BF 6
C6 6
C5 6
B6 4
C2 4
C7 3

Símbolos
Frecuencia
(longitud = 2)
B7BE 8
B7C0 7
B3BE 5
C1C0 4
C4C1 4
C4B3 4

Empecemos a probar posibles substituciones. Vemos que B7 y


B3 son las que tienen más ocurrencia y son buenas candidatas
para las letras e y a. En cuanto a las combinaciones de longitud
dos, B7BE y B7C0 son las de mayor frecuencia. Como
supusimos que B7 y B3 pueden ser e-a o a-e, el resultado de
estas combinaciones de letras podría ser al, el, o en. También
podemos ver en nuestra lista de frecuencias para el castellano
que la o es uno de los caracteres de mayor aparición, por lo que

106 Almacenando contraseñas


podemos usarlo para reemplazar C1. Probemos una primera
substitución con:

Símbolo Substitución
B7 E
B3 A
BE L
C0 N
C1 O

Veamos cómo va cambiando el criptograma al aplicar estas


substituciones. Las letras en minúsculas son nuestras
substituciones y las que están en mayúsculas las que aún
pertenecen al criptograma:

CBenelBFoBFenC6oenC3C7elleB9aC4onalaC6eC4C4aCCa
laBFBBC4aB6aC5eleC5C2eC4B6BBoB6eC2C4onC6oBFaC5a
llaB6elC2alBFeC4alenelBAoC4BBCConC6eBBnBFenC5oa
BCanBBneleC2aC4eB5BBoC3C7eelB5BBeloenC6eC4oC4eC
5onaB4aenC7nanoC6aB8C4aB9oC4oC5aCBB4C4eC8e

Otra técnica que se puede aplicar en este momento es usar una


lista de palabras en el idioma en que está escrito el mensaje para
buscar palabras candidatas. Al principio del criptograma
tenemos:

CBenelBFoBFenC6oenC3

Suponemos que enel es “en el” y el en fnal es la preposición


“en”, entonces BFoBFenC6o es una palabra completa. Usando

Almacenando contraseñas 107

Das könnte Ihnen auch gefallen