héroe bg sin separador
Pautas

Asignación masiva

En este momento, analizaremos las vulnerabilidades de las asignaciones masivas y su aspecto, junto con algunas formas de evitarlas. En primer lugar, un resumen rápido:

La asignación masiva es una vulnerabilidad en la que los puntos finales de la API no restringen qué propiedades de su objeto asociado puede modificar un usuario.

Esta vulnerabilidad puede producirse cuando se utiliza una biblioteca/marco que permite el enlace automático de parámetros HTTP a un modelo que luego se utiliza sin ninguna validación.

El uso del enlace automático de una solicitud a un objeto a veces puede resultar extremadamente útil, pero también puede generar problemas de seguridad si el modelo tiene propiedades que no están destinadas a ser accesibles para el usuario.

Ejemplo

Vamos a usar el ejemplo de una página web en la que un usuario puede cambiar detalles, como su nombre, dirección de correo electrónico y otras cosas similares. Tenemos un modelo de usuario definido como:

clase pública UserModel {

ID largo público {get; set;}
nombre de cadena pública {get; set;}
cadena pública passwordHash {get; set;}
cadena pública emailAddress {get; set;}
libro público isAdmin {get; set;}

}

La parte frontal define un formulario de la siguiente manera. Tenga en cuenta la ausencia del valor `isAdmin`:

<form method="POST">
<input name="Id" type="hidden">
<input name="Name" type="text">
<input name="EmailAddress" type="text">
<input type="submit">
</form>

El controlador define un punto final de la siguiente manera. Al tener el `UserModel` como parámetro, nuestro marco asignará automáticamente las propiedades respectivas a este modelo para nosotros:

[Publicación HTTP]
public bool updateUser (modelo UserModel)
{
//Asegúrese de que el usuario solo se actualice a sí mismo
model.id = request.user.userId;

var success = UserService.UpdateUser (modelo);

devolución exitosa;
}

A partir de aquí, podemos suponer que el método 'UserService.UpdateUser' no realiza ninguna validación adicional en términos de autorización y simplemente guarda el objeto de usuario proporcionado.

(Si no se proporciona ningún valor para una propiedad, solo conserva el valor existente)

Esto significa que un usuario podría enviar una solicitud con el 'isAdmin', lo que anularía el valor actual y convertiría al usuario en administrador de la siguiente manera:

<form method="POST">
<input name="Id" type="hidden" value="666">
<input name="Name" type="text" value="Bad guy">
<input name="EmailAddress" type="text" value="hacker@attacker.com">
<input name="IsAdmin" type="hidden" value="true">
<input type="submit">
</form>

Estrategias de mitigación

A continuación se presentan algunas estrategias de mitigación a tener en cuenta cuando se trata de evitar las vulnerabilidades de asignación masiva.

Evite la reutilización de modelos de datos para modelos de solicitud

Es importante mantener los modelos de datos (que pueden persistir en una base de datos) separados de los modelos que se utilizan al comunicarse con un cliente. Gestionar el envío de un formulario a un controlador es un problema muy diferente al de la persistencia de los datos en una base de datos y la forma en que se representan en la base de datos. Esto crea un nivel de acoplamiento mucho más alto entre la interfaz y la capa de persistencia de lo que es normal.

Sé explícito en tus mapeos

El problema con el enlace (o mapeo) automático es que la falta de mapeos explícitos facilita la exposición de propiedades que no están destinadas a ser accesibles en el modelo. Si eres explícito en las asignaciones entre los modelos de solicitud y el resto de tu backend, puedes evitar este tipo de exposiciones desde el principio.

Esto podría hacerse mediante el uso de diferentes modelos para las solicitudes y los datos. Esto no le impide utilizar un mapeador automático entre la solicitud y el modelo de datos, ya que su modelo de solicitud no debe mostrar propiedades que no estén permitidas para una solicitud específica.

Más ejemplos

A continuación, tenemos algunos ejemplos adicionales en diferentes idiomas de cómo puede ser esto.

C# - inseguro

usuario de clase pública {
ID largo público {get; set;}
cadena pública FirstName {get; set;}
cadena pública LastName {get; set;}
cadena pública passwordHash {get; set;}
cadena pública Country {get; set;}
cadena pública Role {get; set;}
}

[Publicación HTTP]
public ViewResult Edit (usuario usuario)
{
//Simplemente guarda al usuario tal como se proporciona
UserService.UpdateUser (usuario);
devuelve Ok ();
}

C# - seguro

usuario de clase pública {
ID largo público {get; set;}
cadena pública FirstName {get; set;}
cadena pública LastName {get; set;}
cadena pública passwordHash {get; set;}
cadena pública Country {get; set;}
cadena pública Role {get; set;}
}
clase pública UpdateUserViewModel {

cadena pública FirstName {get; set;}
cadena pública LastName {get; set;}
cadena pública Country {get; set;}
}

[Publicación HTTP]
public ViewResult Edit (UpdateUserViewModel UserModel)
{
var user = Request.User;

User.firstName = UserModel.firstName;
User.LastName = UserModel.LastName;
user.country = userModel.country;

UserService.UpdateUser (usuario);

devuelve Ok ();
}

C# - alternativa - excluye parámetros

usuario de clase pública {
ID largo público {get; set;}
cadena pública FirstName {get; set;}
cadena pública LastName {get; set;}
cadena pública passwordHash {get; set;}
cadena pública Country {get; set;}
cadena pública Role {get; set;}
}

[Publicación HTTP]
public ViewResult Edit ([Bind (Include = «Nombre, apellidos, país»)] Usuario)
{
si (Request.User.Id! = User.id) {
devolución Prohibida («Solicitar el cambio de otro usuario»);
}
var ExistingUser = Request.User;
user.passwordHash = User.passwordHash existente;
User.role = User.role existente;

UserService.UpdateUser (usuario);

devuelve Ok ();
}

Java: inseguro

usuario de clase pública {
ayuda pública;
cadena pública FirstName;
apellido de cadena pública;
cadena pública passwordHash;
país de cadena público;
rol de cadena pública;
}

@RequestMapping (valor = «/UpdateUser», método = RequestMethod.post)
cadena pública updateUser (usuario usuario) {
UserService.update (usuario);
devuelve «UserUpdatedPage»;
}


Java: seguro

clase pública UserViewModel {
cadena pública FirstName;
apellido de cadena pública;
país de cadena público;
}
usuario de clase pública {
ayuda pública;
cadena pública FirstName;
apellido de cadena pública;
cadena pública passwordHash;
país de cadena público;
rol de cadena pública;
}
@RequestMapping (valor = «/UpdateUser», método = RequestMethod.post)
cadena pública updateUser (@AuthenticationPrincipal User CurrentUser, UserViewModel UserViewModel) {
CurrentUser.firstName = UserViewModel.firstName;
CurrentUser.LastName = UserViewModel.LastName;
currentUser.country = UserViewModel.country;

UserService.update (usuario actual);
devuelve «UserUpdatedPage»;
}

Javascript: inseguro

app.get ('/user/update', (req, res) => {
var user = req.user;

Object.assign (usuario, req.body);

UserService.update (usuario);

devuelve «El usuario se ha actualizado»;
})

Javascript: seguro

app.get ('/user/update', (req, res) => {
var user = req.user;

User.firstName = req.body.FirstName;
User.lastName = req.body.LastName;
user.country = req.body.country;

UserService.update (usuario);

devuelve «El usuario se ha actualizado»;
})

Python: inseguro

@app .route («/user/update», métodos= ['POST'])
def update_user ():

usuario = request.user
form = request.form.to_dict (flat=True)

para la clave, el valor en form.items ():
setattr (usuario, clave, valor)

UserService.UpdateUser (usuario)

redireccionamiento de retorno («/user», código=201)

Python: seguro

@app .route («/user/update», métodos= ['POST'])
def update_user ():

usuario = request.user
formulario = request.form

User.firstName = form.firstName
User.lastName = form.LastName

UserService.UpdateUser (usuario)
redireccionamiento de retorno («/user», código=201)