Los peligros de setattr: cómo evitar vulnerabilidades de asignación masiva en Python
Jack Hair
15 de febrero de 2023
0 minutos de lecturaLa asignación masiva, también conocida como vinculación automática o inyección de objetos, es una categoría de vulnerabilidades que se producen cuando la entrada del usuario se vincula a variables u objetos dentro de un programa.
Las vulnerabilidades de asignación masiva suelen ser el resultado de que un atacante agregue campos inesperados a un objeto para manipular la lógica de un programa. Esto causó una famosa vulnerabilidad de autenticación de GitHub, en la que la funcionalidad de asignación masiva de Ruby on Rails permitió que un atacante agregara su clave pública a la organización rails de GitHub y enviara un commit a master.
Esta publicación se inspiró, en parte, en la vulnerabilidad Spring4Shell, una vulnerabilidad de ejecución remota de código (RCE) causada por la funcionalidad de vinculación automática incluida en el framework Spring. Esto motivó una investigación para determinar si podía encontrarse una vulnerabilidad similar en el ecosistema de Python.
En esta publicación, analizaremos los posibles riesgos de la función setattr() de Python y veremos cómo prevenir vulnerabilidades de asignación masiva en tu aplicación.
Presentamos setattr
Una forma común de implementar la asignación masiva en Python es usar la función integrada setattr(). Esta función recibe un objeto, el nombre de un atributo y su valor, y asigna ese valor al atributo del objeto. Puede agregar atributos nuevos y modificar los existentes.
Aunque es práctico, permitir que un usuario establezca el valor de cualquier atributo de un objeto puede tener consecuencias imprevistas.
En primer lugar, puede dar lugar a las vulnerabilidades tradicionales de lógica por asignación masiva en un programa. Ahora se pueden modificar campos de un objeto que no estaban destinados a ser modificados por un usuario.
En segundo lugar, los atributos que se pueden modificar con setattr no se limitan a los campos definidos por el programador: también se pueden establecer funciones y atributos integrados como __class__ y __dict__ . A diferencia de la vulnerabilidad Spring4Shell, setattr no puede establecer atributos secundarios anidados de un objeto, lo que limita el alcance de un ataque. Sin embargo, es fácil provocar una excepción cuando se llama a setattr, asignando un valor de cadena a __class__, o más adelante durante la ejecución del programa, sobrescribiendo una función del objeto. Provocar una excepción de esta manera puede dar lugar a un comportamiento anormal del programa o a una denegación de servicio.
El código de abajo es un pequeño ejemplo que usa setattr para crear un usuario. Si se ejecuta con la entrada first_name=Sam,last_name=S, el resultado será Sam S: Permission denied.
Para hacer que el programa falle debido a una excepción no controlada, se puede usar la entrada first_name=Sam,last_name=S,is_admin=foo. Esta sobrescribirá la función is_admin y la reemplazará por una cadena, lo que provocará una excepción cuando se llame a la función.
La entrada first_name=Sam,last_name=S,admin=foo sobrescribe el atributo admin del objeto User con la cadena foo, porque este es un valor verdadero (Truthy). Cuando la función is_admin comprueba el atributo admin, el usuario se trata como administrador y se muestra Sam S: You are admin!.
Cómo prevenir la asignación masiva
Una estrategia general para mitigar las vulnerabilidades de asignación masiva consiste en mantener separados los objetos que contienen entradas del usuario de los objetos responsables de la lógica interna del programa. Para ello, se crean objetos de transferencia de datos (DTO), que solo contienen los campos que un usuario puede establecer. En Python, esto es fácil de hacer con dataclasses. El siguiente código muestra cómo se puede usar un DTO para definir explícitamente los campos permitidos. Si se especifican campos incorrectos, se genera un TypeError, que luego se captura y se maneja para prevenir los ataques mencionados.
Validar la entrada del usuario también es una forma de prevenir las vulnerabilidades de asignación masiva. Como siempre, es mejor validar con una lista de datos aprobados en lugar de buscar datos prohibidos. El siguiente código sigue usando setattr, pero compara el nombre del atributo con una lista de atributos permitidos.
Siempre es mejor especificar explícitamente qué entradas del usuario espera un programa, pero si necesitas más flexibilidad para manejarlas, existen alternativas a la función setattr. El módulo collections de la biblioteca estándar de Python ofrece dos clases útiles para ayudarte: collections.UserDict y collections.abc.MutableMapping. Se puede heredar de estas clases para agregar funcionalidad a las asignaciones de clave y valor. Si necesitas acceder a los valores mediante atributos en lugar de claves, puedes hacerlo implementando un método __getattr__ en tu clase. En el siguiente ejemplo, la clase User funciona como un diccionario y puede almacenar cualquier campo proporcionado por el usuario. Al sobrescribir __getattr__, cuando se accede a un atributo que no se encuentra mediante el mecanismo habitual de acceso a atributos, este se devuelve desde el diccionario de campos proporcionados por el usuario. Así, se puede acceder a estos campos de la misma manera que a los atributos, pero se evita que la entrada del usuario sobrescriba los atributos de la clase.
Protege tus bibliotecas de Python
En pleno desarrollo de software, puede ser difícil encontrar el equilibrio entre la seguridad, la comodidad y la flexibilidad. Algo que parece inofensivo a primera vista puede exponerte a ti y a tu aplicación a diversos riesgos de seguridad; por eso son tan importantes la educación y los métodos alternativos. Hemos explicado cómo funciona setattr y los peligros de depender de esta función en tus bibliotecas de Python, además de varias formas de prevenir vulnerabilidades de asignación masiva. Con estos conocimientos, estás más cerca de proteger tus aplicaciones de Python contra ataques y filtraciones de datos. Para obtener más información, visita nuestra página de inicio y crea una cuenta gratuita, o agenda una demostración para ver cómo Snyk puede ayudar a proteger tu código, contenedores, dependencias e infraestructura en la nube.
Empieza con los desafíos de Capture the Flag
Aprende a resolver desafíos de captura la bandera con nuestro taller virtual introductorio a pedido.



