sábado, 27 de febrero de 2010

Cómo se almacenan datos de sesión a través de cookies en rails

Hace algún tiempo estuve leyendo sobre la seguridad de los datos almacenados en sesion en rails. En la actualidad, existen dos modos de almacenamiendo: en cookies o en base de datos. Pues bien, los datos de sesión almacenados en cookies, son totalmente visibles por cualquier usuario un tanto hábil. Sin embargo, esto no quiere decir que almacenar datos en cookies no sea seguro. Veamos el proceso de almacenamiento usado por rails, de forma que al final del texto el lector sea capaz de sacar sus propias conclusiones acerca de ello. Por supuesto, expondré las mias.

Generación del sid (session ID)
Si no existe sesión, una nueva es creada y es cuando rails invoca el método generate_sid:
# actionpack/lib/action_controller/session/cookie_store.rb
def generate_sid
ActiveSupport::SecureRandom.hex(16)
end
Como puede verse, generate_sid devuelve una cadena hexadecimal y aleatoria de longitud 16. Esta función no es invocada de nuevo mientras dure la sesión actual.

Message Verifier
Para generar el valor de la cookie, Rails crea una instancia de ActiveSupport::MessageVerifier. MessageVerifier hace dos cosas: por un lado genera el mensaje a almacenar en la cookie; por otro, añade una cadena "message digest" al final de lo anterior. Así, el mensaje final está formado por por las dos partes anteriores, separadas por -- . El método que genera este contenido es:
def generate(value)
data = ActiveSupport::Base64.encode64s(Marshal.dump(value))
"#{data}--#{generate_digest(data)}"
end

Como puede verse, value es el valor que queremos almacenar en la cookie. En general, Rails almacena un hash con una serie de datos. Marshal nos permite pasar un objeto Ruby a cadena para por ejemplo, almacenarlo en una cookie, y posteriormente con esa cadena poder volver obtener el objeto Ruby original, en nuestro caso un hash. Por último esta cadena es codificada en base 64.

Generate Digest y conclusiones
Generate digest nos genera una cadena, a partir de la cadena que teníamos inicialmente (la que queremos almacenar en la cookie). También hace uso de una clave secreta que podemos encontrar en initializers/session_store.rb .
Por tanto, si nos vamos al contenido de la cookie y le hacemos un simple decode64, podemos ver (aunque con dificultad) el contenido de la session_id, o incluso otros datos almacenados (como user_id, éste muy frecuente). Está bien, ¿y podemos hacer algo con este user_id? Pues la verdad es que no, nada, excepto tener conocimiento de él. Pero...si el sistema de autenticación usa este user_id, ¿podemos cambiar el valor de la cookie y pasar a estar en el sistema como otro usuario? La respuesta es NO. ¿Por qué? Pues simple, porque si cambia el contenido de la primera parte de la cadena almacenada en la cookie, también deberá cambiar la segunda (la generada por generate digest) y a no ser que tengamos acceso a la clave almacenada en initializers/session_store.rb este proceso no nos será posible. Rails básicamente lo que hace es, al leer la sesión, comprobar que esta segunda parte es correcta. Si no lo es, sabrá que algo va mal... Por tanto es seguro, en tanto que un usuario malicioso no nos podrá cambiar su contenido. Sin embargo, no ofrece ninguna seguridad para leer su contenido, por lo que nunca se deberá usar para almacenar datos sensibles, tales como contraseñas, números de cuentas bancarias, etc.