Advertisement
  1. Code
  2. Ruby

Asignación masiva, Rails y tú

Scroll to top
Read Time: 8 mins

Spanish (Español) translation by steven (you can also view the original English article)

A principios de 2012, un desarrollador, llamado Egor Homakov, aprovechó un agujero de seguridad en Github (una aplicación Rails) para obtener acceso de confirmación al proyecto Rails.

Su intención era principalmente señalar un problema de seguridad común con muchas aplicaciones de Rails que resulta de una función, conocida como asignación masiva (y lo hizo en voz alta). En este artículo, revisaremos qué es la asignación masiva, cómo puede ser un problema y qué puedes hacer al respecto en tus propias aplicaciones.


¿Qué es la asignación masiva?

Para comenzar, primero echemos un vistazo a lo que significa la asignación masiva y por qué existe. A modo de ejemplo, imaginemos que tenemos la siguiente clase User en nuestra aplicación:

La asignación masiva nos permite establecer varios atributos a la vez:

Sin la conveniencia de la asignación masiva, tendríamos que escribir una declaración de asignación para cada atributo para lograr el mismo resultado. He aquí un ejemplo:

Obviamente, esto puede volverse tedioso y doloroso; así que nos inclinamos a los pies de la pereza y decimos, sí, sí, la asignación masiva es algo bueno.


El problema (potencial) con la asignación masiva

Un problema con las herramientas afiladas es que puedes cortarte con ellas.

¡Pero espera! Un problema con las herramientas afiladas es que puedes cortarte con ellas. La asignación masiva no es una excepción a esta regla.

Supongamos ahora que nuestra pequeña aplicación imaginaria ha adquirido la capacidad de disparar misiles. Como no queremos que el mundo se convierta en cenizas, agregamos un campo de permiso booleano al modelo User para decidir quién puede disparar misiles.

Supongamos también que tenemos un formulario para que los usuarios editen su información de contacto: este podría ser un formulario en algún lugar que sea accesible para el usuario con campos de texto para el nombre, apellido y dirección de correo electrónico del usuario.

Nuestro amigo John Doe decide cambiar su nombre y actualizar su cuenta de correo electrónico. Cuando envía el formulario, el navegador emitirá una solicitud similar a la siguiente:

La acción update dentro de UsersController podría tener un aspecto similar a lo siguiente:

Dada nuestra solicitud de ejemplo, el hash de params se verá similar a:

Ahora digamos que NewJohn se vuelve un poco astuto. No necesita necesariamente un navegador para emitir una solicitud HTTP, por lo que escribe un script que emite la siguiente solicitud:

Los campos, como :admin, :owner y :public_key, se pueden adivinar con bastante facilidad.

Cuando esta solicitud llegue a nuestra acción update, la llamada a update_attributes se verá como {:can_fire_missiles => true} y le dará a NewJohn la capacidad de disparar misiles. ¡Ay de nosotros!

Así es exactamente como Egor Homakov se dio a sí mismo acceso a los "commits" del proyecto Rails. Debido a que Rails tiene tantas convenciones, campos como :admin, :owner y :public_key son fáciles de adivinar. Además, si no hay protecciones en su lugar, puedes obtener acceso a cosas que se supone que no debes poder tocar.


Cómo lidiar con la asignación masiva

Entonces, ¿cómo nos protegemos de la asignación masiva sin sentido? ¿Cómo podemos evitar que los NewJohns del mundo disparen nuestros misiles con un abandono imprudente?

Afortunadamente, Rails proporciona un par de herramientas para gestionar el problema: attr_protected y attr_accessible.

attr_protected: La lista negra

Con attr_protected, puedes especificar qué campos nunca se pueden asignar de forma masiva:

Ahora, cualquier intento de asignar en masa el atributo can_fire_missiles fallará.

attr_accessible: La lista blanca

El problema con attr_protected es que es demasiado fácil que te olvides de agregar un campo recién implementado a la lista.

Aquí es donde entra en juego attr_accessible. Como habrás adivinado, es lo opuesto a attr_protected: solo enumera los atributos que deseas que sean asignables en masa.

Como tal, podemos cambiar nuestra clase User a este enfoque:

Aquí, enumeramos explícitamente lo que se puede asignar en masa. Todo lo demás será rechazado. La ventaja aquí es que si, por ejemplo, agregamos una marca admin al modelo User, este estará automáticamente a salvo de la asignación masiva.

Como regla general, deberías preferir attr_accessible a attr_protected, ya que te ayuda a pecar de precavido.

Roles con asignación masiva

Rails 3.1 introdujo el concepto de "roles" con asignación masiva. La idea es que puedas especificar diferentes listas attr_protected y attr_accessible para diferentes situaciones.

Configuración para toda la aplicación

Puedes controlar el comportamiento de la asignación masiva en tu aplicación editando la configuración config.active_record.whitelist_attributes dentro del archivo config/application.rb.

Si se establece en false, la protección de asignación masiva solo se activará para los modelos en los que especifiques una lista attr_protected o attr_accessible.

Si se establece en true, la asignación masiva será imposible para todos los modelos a menos que especifiques una lista attr_protected o attr_accessible. Ten en cuenta que esta opción está habilitada de forma predeterminada desde Rails 3.2.3 en adelante.

Rigor

A partir de Rails 3.2, existe además una opción de configuración para controlar el rigor de la protección de asignación masiva: config.active_record.mass_assignment_sanitizer.

Si se establece en :strict, se generará un ActiveModel::MassAssignmentSecurity::Error cada vez que tu aplicación intente asignar en masa algo que no debería. Deberás manejar estos errores explícitamente. A partir de la versión 3.2, esta opción está configurada para ti en los entornos de desarrollo y prueba (pero no de producción), presumiblemente para ayudarte a rastrear dónde podrían estar los problemas de asignación masiva.

Si no se configura, manejarás la protección de asignación masiva en silencio, lo que significa que solo configurarás los atributos que se supone que deben asignarse, pero no generará un error.


"Strong Parameters" en Rails 4: Un enfoque diferente

La seguridad de las asignaciones masivas se trata realmente de manejar entradas que no son de confianza.

El incidente de Homakov inició una conversación sobre la protección de asignaciones masivas en la comunidad de Rails (y también en otros lenguajes); se planteó una pregunta interesante: ¿La seguridad de asignación masiva pertenece a la capa del modelo?

Algunas aplicaciones tienen requisitos de autorización complejos. Tratar de manejar todos los casos especiales en la capa del modelo puede comenzar a sentirse torpe y demasiado complicado, especialmente si te encuentras cubriendo roles por todas partes.

Una idea clave aquí es que la seguridad de las asignaciones masivas se trata realmente de manejar entradas que no son de confianza. A medida que una aplicación Rails recibe información del usuario en la capa del controlador, los desarrolladores comenzaron a preguntarse si sería mejor tratar el problema allí en lugar de los modelos ActiveRecord.

El resultado de esta discusión es la gema Strong Parameters, disponible para su uso con Rails 3, y por defecto en la próxima versión de Rails 4.

Suponiendo que nuestra aplicación de misiles está construida sobre Rails 3, así es como podríamos actualizarla para usarla con la gema "strong parameters":

Añadir la gema

Agrega la siguiente línea al Gemfile:

Desactivar la protección de asignación masiva basada en modelos

Dentro de config/application.rb:

Cuéntale a los modelos

Actualizar los controladores

Ahora, si intentas algo como user.update_attributes(params), obtendrás un error en tu aplicación. Primero debes llamar a permit en el hash de params con las claves que están permitidas para una acción específica.

La ventaja de este enfoque es que debes ser explícito sobre qué entrada aceptas en el momento en que estás tratando con la entrada.

Nota: Si se tratara de una aplicación Rails 4, el código del controlador es todo lo que necesitaríamos; la funcionalidad de "strong parameters" se integrará de forma predeterminada. Como resultado, no necesitarás incluirlo en el modelo o la gema separada en el Gemfile.


Conclusión

La asignación masiva puede ser una característica increíblemente útil al escribir código en Rails. De hecho, es casi imposible escribir código en Rails razonable sin él. Desafortunadamente, la asignación masiva sin sentido también está plagada de peligros.

Con suerte, ahora estás equipado con las herramientas necesarias para navegar de manera segura en las aguas de asignación masiva. ¡Por menos misiles!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.