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.

domingo, 6 de diciembre de 2009

CruiseControl

CruiseControl es una herramienta de integración continua escrita en, y para, ruby. Su finalidad básica es la de alertar a los miembros de un proyecto software cuando un cambio en el repositorio de control del código fuente (como svn) rompe dicho proyecto. En el caso de RoR, lo que hará periódicamente será la ejecución de los test de la aplicación, informando del resultado.

Es muy fácil de usar. Basta con descargarlo (es un mini-proyecto RoR), añadirle nuestro proyecto (indicando el repositorio del código fuente), y arrancarlo. Por defecto arranca en el puerto 3333, y si accedemos nos encontraremos algo como esto. Además, podemos integrar rcov en cruisecontrol y obtener información del code coverage de nuestro proyecto.

Usando rcov desde cuisecontrol

Para que CruiseControl nos facilite información sobre el Code Coverage, necesitamos:
  1. Gema rcov ('sudo gem install rcov'). Comprobamos ejecutando 'rcov' en el terminal.
  2. Plugin rails_rcov ( './script/plugin install http://svn.codahale.com/rails_rcov' --> Doc: http://agilewebdevelopment.com/plugins/rails_rcov)
  3. Añadir en lib/tasks/ el fichero cruise.rake con el siguiente contenido:

# Set the artifacts dir for development
ENV['CC_BUILD_ARTIFACTS'] ||= File.expand_path("#{RAILS_ROOT}/metrics")
ENV['RAILS_ENV'] = 'test'

# This hack is needed because db:test:purge implementation for MySQL drops the test database, invalidating
# the existing connection. A solution is to reconnect again.
def reconnect
require 'active_record' unless defined? ActiveRecord
configurations = ActiveRecord::Base.configurations
if configurations and configurations.has_key?("test") and configurations["test"]["adapter"] == 'mysql'
ActiveRecord::Base.establish_connection(:test)
end
end

desc 'Continuous build target'
task :cruise do
# Add local user gem path, in case rcov was installed with non-root access
#ENV['PATH'] = "#{ENV['PATH']}:#{File.join(Gem.user_dir, 'bin')}"

puts
puts "[CruiseControl] Build environment:"
puts "[CruiseControl] #{`cat /etc/issue`}"
puts "[CruiseControl] #{`uname -a`}"
puts "[CruiseControl] #{`ruby -v`}"
`gem env`.each_line {|line| print "[CruiseControl] #{line}"}
puts "[CruiseControl] Local gems:"
`gem list`.each_line {|line| print "[CruiseControl] #{line}"}
puts

out = ENV['CC_BUILD_ARTIFACTS']
mkdir_p out unless File.directory? out if out

if File.exists?(Dir.pwd + "/config/database.yml")
if Dir[Dir.pwd + "/db/migrate/*.rb"].empty?
raise "No migration scripts found in db/migrate/ but database.yml exists, " +
"CruiseControl won't be able to build the latest test database. Build aborted."
end

# perform standard Rails database cleanup/preparation tasks if they are defined in project
# this is necessary because there is no up-to-date development database on a continuous integration box
if Rake.application.lookup('db:test:purge')
Rake::Task['db:test:purge'].invoke
end
if Rake.application.lookup('db:migrate')
reconnect
Rake::Task['db:migrate'].invoke
end
end

if RUBY_VERSION == '1.8.7'
puts '!!!!!! Skipping rcov on Ruby 1.8.7'
Rake::Task["test:units"].invoke
Rake::Task["test:functionals"].invoke
else
ENV['SHOW_ONLY'] = 'models,lib,helpers'
Rake::Task["test:units:rcov"].invoke
mv 'coverage/units', "#{out}/unit test coverage" if out

ENV['SHOW_ONLY'] = 'controllers'
Rake::Task["test:functionals:rcov"].invoke
mv 'coverage/functionals', "#{out}/functional test coverage" if out
end
end

Para probar que funciona correctamente y que cruise-control no va a fallar, podemos probar a ejecutar:
rake cruise

sábado, 14 de noviembre de 2009

Google libera Closure, la librería JavaScript detrás de Gmail y Gmaps

Me hago eco de la siguiente noticia, leída en javahispano.org:

Google ha liberado una librería y un conjunto de herramientas para trabajar con JavaScript: Closure. Closure es empleada por aplicaciones como Gmail y Gmaps. Está formado por un optimizador que permite comprimir el código JavaScript, y a la vez realiza un análisis sintáctico del código para encontrar errores comunes.

Por otro lado está una librería con utilidades para manipular el DOM, comunicarse con el servidor, crear animaciones, soporte para edición de texto... La librería es completamente "multi navegador", su diseño es modular y (obviamente, dada las aplicaciones en las que ha sido empleada) está muy bien testada.

Por último, tenemos un sistema de plantillas tanto para Java como para JavaScript cuyo propósito es simplificar la tarea de generar dinámicamente HTML. Las plantillas están implementadas tanto en Java como en JavaScript, lo que permite emplearlas tanto el servidor como en el cliente.

lunes, 28 de septiembre de 2009

Testing con "redirect_to :back"

Si necesitas testear un controlador rails que contiene un "redirect_to :back" obtendrás un error al lanzar el test. Si vamos a la implementación, podemos encontrar:

  • :back - Back to the page that issued the request. Useful for forms that are triggered from multiple places. Short-hand for redirect_to(request.env["HTTP_REFERER"])
Si estamos testeando no tendremos un request, por lo que fallará la redirección y por lo tanto el test.

SOLUCIÓN:

def test_should_get_new
   @request.env['HTTP_REFERER'] = 'http://whatever'
   get :new, :link_id => 1
   assert_redirected_to 'http://whatever'
end

miércoles, 26 de agosto de 2009

¿Qué licencia Open Source elijo?

Me llega la hora (por fin :) ) de la entrega de mi proyecto fin de carrera. Lo he llamado GASWED, o Gestor de Actualizaciones Software Web en Entornos Distribuidos. Básicamente consiste en un gestor que, ayudándose de svn (subversion), controla revisión de los distintos ficheros de un proyecto software web en distintos servidores y, además, está orientado a paquetes. Es decir, no trabajamos con ficheros individuales como con svn, sino con paquetes de éstos tales que agrupen alguna funcionalidad (tablón, foros...). Además, cada paquete puede ser importado en distintos servidores o eliminado, llevando asociadas sentencias sql que serán ejecutadas en las bases de datos de cada uno de estos servidores. Sí, prometo una explicación más detallada cuando lo finalize y entregue ;) .

Si tú tambien eres programador y estás desarrollando una aplicación a la que le quieres aplicar una licencia Open Source, seguramente te hayas dado cuenta de que hay muchas opciones. ¿Cuál es la mejor elección a la hora de trabajar con Open Source? Es en lo que trata de ayudarnos un completo artículo en el que un desarrollador ha analizado cada una de las licencias Open Source que existen actualmente.

Shlomi Fish, un joven desarrollador especializado en desarrollos Open Source, ha elaborado un interesante artículo en el cual compara las distintas alternativas que los programadores de Software Libre y Open Source (Free and Open Source Software, FOSS) tienen cuando están trabajando en un nuevo proyecto.

Lo cierto es que este tipo de decisión cada vez es más compleja debido al creciente número de licencias que han ido apareciendo con el paso del tiempo. Aunque la GPL es la más conocida y extendida -sobre todo en su versión 2- en los últimos años distintas organizaciones han propuesto otro tipo de licencias que ofrecían ciertas ventajas con respecto a otras alternativas, de modo que básicamente cada programador disponía de una licencia que más o menos se ajustaba a sus necesidades.

Fish elabora un completo análisis dividiendo el actual segmento de las licencias FOSS en tres grandes grupos:
  1. Licencias de Dominio Público: permiten hacer prácticamente cualquier cosa que te convenga con el software y su código fuente. Ejemplos de estas licencias: licencias BSD, MIT X11, Apache e ISC.
  2. Licencias Copyleft "débiles": este tipo de licencias obligan que el código fuente derivado de estos desarrollos siga usando la misma licencia, aunque uno puede enlazar esa licencia débil e integrarla en un desarrollo global con otra licencia -incuyendo licencias que no son Open Source. Ejemplos: LGPL, Artistic Licence, o la MPL.
  3. Licencias Copyleft "fuertes": parecidas a las anteriores, pero que obligan a que incluso desarrollos que incluyan a código fuente con esa licencia tengan que usar esas licencias, y no otras. Ejemplos: la famosa GPL (tanto en su versión 2 como en su versión 3), o la Sleepycat License.
A partir de ahí Fish realiza una serie de recomendaciones en las que por ejemplo indica que es mejor utilizar licencias compatibles con la GPL, pero que no es buena idea usar las licencias GPL o LGPL porque contienen demasiadas restricciones adicionales al concepto de copyleft. Según su opinión, la licencia MIT X11 es "corta, simple y fácil de comprender", y además es compatible con la GPL.

Personalmente, creo que me voy a decidir por una licencia GPL en su versión 3. Como gustos, colores.

EDITED:
Por fin está el proyecto acabado y entregado. Finalmente me decidí por AGPL v3.

¿Es seguro el DNI electrónico?

ElDNI electrónico es el nuevo carnet de identidad de los españoles. Una de las grandes novedades es la incorporación de un chip que permite realizar operaciones de firma electrónica e identificar a los ciudadanos a través de Internet. El DNI-e sitúa a España en la vanguardia mundial en cuanto a firma digital y permitirá agilizar enormemente muchos de los trámites administrativos que hasta hace bien poco solo podían ser realizados en persona o bien requerían de engorrosos trámites técnico-burocráticos -obtención de un certificado, etc-.

Pero... ¿Son totalmente seguras todas estas operaciones? ¿Están nuestros datos personales totalmente seguros? Un estudio llevado a cabo por PenTest nos demuestra que tenemos que andarnos con mucho ojo. Podemos ver este estudio aquí. No tiene desperdicio.