#358 Brakeman
- Download:
- source codeProject Files in Zip (129 KB)
- mp4Full Size H.264 Video (25.5 MB)
- m4vSmaller H.264 Video (13.2 MB)
- webmFull Size VP8 Video (12.3 MB)
- ogvFull Size Theora Video (34.2 MB)
A continuación se muestra una captura de pantalla de una aplicación que tiene varias vulnerabilidades de seguridad. Puede ser complicado encontrarlas, por lo que en este episodio vamos a utilizar la gema Brakeman que sirve para avisar de problemas típicos de seguridad.
Brakeman analiza el código Ruby de la aplicación Rails en busca de problemas habituales que podrían presentar un problema de seguridad. No es infalible, pero desde luego ayuda a localizar algunas vulnerabilidades, por lo que lo utilizaremos en nuestra aplicación para ver qué es lo que puede encontrar.
En marcha con Brakeman
Brakeman se instala de la manera habitual.
$ gem install brakeman
Una vez que se ha instalado la gema tendremos disponible la herramienta brakeman
(es posible que quienes estén usando rbenv tengan que ejecutar rbenv rehash
) y podemos lanzarlo desde el directorio de nuestra aplicación Rails. Brakeman analizará el código fuente y emitirá un informe.
$ brakeman [Notice] Detected Rails 3 application Loading scanner... [Notice] Using Ruby 1.9.3. Please make sure this matches the one used to run your Rails application. Processing application in /Users/eifion/store # Se omite el resto de la salida
En nuestra aplicación vemos que Brakeman ha encontrado algunos problemas, pero antes de empezar a resolverlos generaremos una versión HTML del informe. Esto puede hacerse con la opción -o
, de forma que si el nombre de fichero termina en .html
el formato del informe será HTML.
$ brakeman -o brakeman.html
En la parte superior del fichero se ve la lista de tests que ha llevado a cabo Brakeman, seguida de un resumen con los resultados.
La parte más interesante del informe viene en la parte inferior. A continuación se muestra una lista con los errores encontrados.
La primera columna es una medida de la confianza, lo que nos indica cómo de seguro está Brakeman de que ha detectado realmente un problema de seguridad. También se puede ver la ubicación y el tipo de problema encontrado, así como una explicación detallada. Como estamos viendo la versión HTML del informe, se puede hacer clic en cualquier mensaje para ver el código fuente correspondiente.
Corrección de los problemas
Iremos recorriendo todos los problemas de uno en uno y los iremos corrigiendo. Empezaremos con el segundo de la lista, cuyo mensaje nos dice:
All versions of Rails before 3.0.14, 3.1.6, and 3.2.6 contain SQL Injection Vulnerabilities: CVE-2012-2694 and CVE-2012-2695; Upgrade to 3.2.6, 3.1.6, 3.0.14
Se trata de un problema específico de la versión de Rails que estamos usando en nuestra aplicación. Nuestra aplicación utiliza Rails 3.2.5, pero en la 3.2.6 se introdujo un importante parche de seguridad. Se trata de un cambio bastante reciente, por lo que es bueno ver que Brakeman está al día de este tipo de incidentes. Para eliminar esta advertencia tan sólo tenemos que actualizar la versión de Rails en el Gemfile
.
gem 'rails', '3.2.6'
Tras hacer esto, ejecutaremos bundle update rails
para instalar la nueva versión de Rails. Si volvemos a generar el informe con Brakeman de nuevo veremos que ha desaparecido la incidencia. El siguiente problema aparece en la acción UsersController#create
.
Unprotected mass assignment near line 7: User.new(params[:user])
Existe un problema relacionado en la sección de advertencias en el modelo.
Mass assignment is not restricted using attr_accessible
Si no estamos seguros del problema al que se refiere el informe podemos mirar la documentación de Brakeman. Hay varios artículos, incluyendo toda una sección dedicada a los tipos de advertencias donde encontraremos información detallada sobre las tipologías de error que puede detectar Brakeman, incluyendo la asignación masiva. En este documento se explica esta vulnerabilidad y cómo resolverla. En Rails 3.1 (y versiones más recientes) podemos corregir este problema fácilmente añadiendo la siguiente línea al fichero de configuración de la aplicación.
config.active_record.whitelist_attributes = true
Este valor se encuentra activado por defecto en las aplicaciones que hayan sido generadas con versiones más nuevas de Rails, pero los que hayan actualizado a partir de versiones anteriores pueden haberlo pasado por alto fácilmente, y es bueno que Brakeman nos avise. Tenemos que acordarnos de añadir attr_accessible
a todos los modelos que no lo tengan, como por ejemplo el modelo User
del que nos avisaba Brakeman.
class User < ActiveRecord::Base has_secure_password attr_accessible :name, :password, :password_confirmation validates_format_of :name, with: /^\w+$/ end
Si volvemos a generar el informe veremos que estas advertencias han desaparecido. El siguiente problema que abordaremos aparece en ProductsController
.
Possible SQL injection near line 3: Product.order("name #{params[:direction]}")
Este tipo de problemas de inyección de SQL son graves, y deben tenerse muy en cuenta. Si miramos en la acción index
de ProductsController
veremos el código mal escrito que hace que Brakeman se queje.
def index @products = Product.order("name #{params[:direction]}") end
Estamos insertando un parámetro directamente en la clásula order
de SQL, lo que es muy mala idea. Se puede corregir esto comprobando el valor del parámetro para chequear si vale asc
o desc
y usando sólo dicho valor en el SQL. Brakeman es lo suficientemente astuto como para darse cuenta de que los valores recibidos por parámetro son peligrosos pero el valor de la cadena que establecemos no lo es.
def index direction = params[:direction] == "desc" ? "desc" : "asc" @products = Product.order("name #{direction}") end
Al volver a generar el informe veremos que el problema ha desaparecido. El último aviso de seguridad es el siguiente:
Possible unprotected redirect near line 12: redirect_to((session.delete(:return_to) or root_url))
Brakeman le ha asignado a este error una confianza baja, por lo que puede que no sea realmente un problema como tal, pero es buena idea investigarlo de todas formas para ver de qué se trata. La documentacion sobre advertencias de redirección incluye más detalles sobre el tipo de vulnerabilidad. Si la redirección está basada en un valor introducido por un parámetro del usuario puede ser que seamos víctimas de ataques de phishing. Una página web malintencionada puede hacerse pasar por la nuestra y los administradores de este sitio podrían utilizar esta vulnerabilidad para redigir a usuarios desde nuestro sitio al suyo. Si alguien inicia la sesión en nuestra aplicación y es redirigido al sitio malicioso podrían preguntarles allí la clave de nuevo. Para proteger nuestro sitio de esta vulnerabilidad podemos utilizar la opción :only_path
que obliga a que la redirección se haga dentro de nuestra aplicación. En el controlador SessionsController
de nuestra aplicación haremos la redirección basándonos en el valor de la variable de sesión return_to
, que se establece de un parámetro de la acción new
.
class SessionsController < ApplicationController def new session[:return_to] = params[:return_to] if params[:return_to] end def create user = User.find_by_name(params[:name]) if user && user.authenticate(params[:password]) session[:user_id] = user.id redirect_url = session.delete(:return_to) || root_url redirect_to redirect_url else flash.now.alert = "Name or password is invalid" render "new" end end def destroy session[:user_id] = nil redirect_to root_url end end
Para protegernos de este problema, utilizaremos la opción only_path
en la redirección.
def create user = User.find_by_name(params[:name]) if user && user.authenticate(params[:password]) session[:user_id] = user.id redirect_url = session.delete(:return_to) || root_url redirect_to redirect_url, :only_path => true else flash.now.alert = "Name or password is invalid" render "new" end end
Si volvemos a generar el informe veremos que la advertencia ha desaparecido.
Nuestro último aviso tiene que ver con el modelo User
.
Insufficient validation for 'name' using /^\w+$/. Use \A and \z as anchors near line 4
Esta advertencia nos dice que estamos usando una validación insuficiente en una expresión regular. Estamos usando símbolos circunflejos y de dólar cuando deberíamos utilizar \A
y \z
. Esto es así porque utilizamos validates_format_of
en el modelo del usuario para comprobar el formato del nombre. Los usuarios podrian engañar esta comprobación insertando retornos de carro en el nombre de usuario porque la expresión sólo comprueba los límites de una línea y no el texto completo
class User < ActiveRecord::Base has_secure_password attr_accessible :name, :password, :password_confirmation validates_format_of :name, with: /^\w+$/ end
Tan sólo tenemos que arreglar la expresión regular para corregir esto:
validates_format_of :name, with: /\A\w+\z/
Ya no aparecerán más mensajes de error si volvemos a generar el informe. ¿Quiere decir esto que la aplicación ya es totalmente segura? Pues por desgracia no, porque hay varias vulnerabilidades que Brakeman no puede detectar, así que es bueno estar siempre pendiente de ellas. Brakeman es una gran herramienta pero debe usarse para encontrar problemas potenciales, nunca nos debemos confiar aumiendo que la aplicación está libre de vulnerabilidades sólo porque Brakeman no las encuentre.
Por supuesto Brakeman hace muchas más cosas. Si miramos las opciones en el README veremos que hay muchas más formas de personalizar el informe que se genera.
Si queremos ejecutar Brakeman como una tarea Rake podemos utilizar la opción --rake
. Esto generará una tarea Rake en nuestra aplicación.
namespace :brakeman do desc "Run Brakeman" task :run, :output_file do |t, args| require 'brakeman' Brakeman.run :app_path => ".", :output_file => args[:output_file], :print_report => true end end
Esta tarea sirve de ejemplo de cómo podemos ejecutar Brakeman desde Ruby. Se le pueden pasar varias opciones para personalizar el informe y hacerlo consistente con la aplicación. Si queremos ejecutar esta orden, tendremos que añadir la gema brakeman
a nuestro Gemfile
.
gem 'brakeman', group: :development
Otra forma de usar Brakeman es con Guard, que ya vimos en el episodio 264. Utilizando la gema Guard Brakeman, Brakeman se ejecutará automáticamente cada vez que cambiemos un archivo.