Directives

Affectation de masse

Nous allons examiner les vulnérabilités liées au Mass Assignment, leur apparence et quelques méthodes pour les éviter. Tout d'abord, un bref rappel :

Le Mass Assignment est une vulnérabilité qui survient lorsque les points de terminaison d'une API ne restreignent pas les propriétés de leurs objets associés pouvant être modifiées par un utilisateur.

Cette vulnérabilité peut se produire lors de l'utilisation d'une bibliothèque ou d'un framework permettant la liaison automatique des paramètres HTTP à un modèle, qui est ensuite utilisé sans aucune validation.

L'utilisation de la liaison automatique d'une requête à un objet peut être extrêmement utile, mais elle peut également entraîner des problèmes de sécurité si le modèle contient des propriétés qui ne sont pas censées être accessibles à l'utilisateur.

Exemple

Prenons l'exemple d'une page web où un utilisateur peut modifier ses informations, comme son nom, son adresse e-mail et d'autres éléments similaires. Nous avons un modèle User défini comme suit :

public class UserModel {

    public long Id { get; set; }
    public string Name { get; set; }
    public string PasswordHash { get; set; } 
    public string EmailAddress { get; set; } 
    public bool IsAdmin { get; set; }

}

La partie frontend définit un formulaire comme suit. Notez l'absence de la valeur `IsAdmin` :

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

Le contrôleur définit un point de terminaison comme suit. En utilisant `UserModel` comme paramètre, notre framework mappera automatiquement les propriétés respectives sur ce modèle pour nous :

[HttpPost]
public bool UpdateUser(UserModel model)
{
    // S'assurer que l'utilisateur ne modifie que son propre profil
    model.Id = Request.User.UserId;

    var success = UserService.UpdateUser(model);

    return success;    
}

À partir de là, nous pouvons supposer que la méthode « UserService.UpdateUser » n'effectue aucune validation supplémentaire en termes d'autorisation et se contente d'enregistrer l'objet utilisateur fourni.

(Si aucune valeur n'est fournie pour une propriété, la valeur existante est conservée)

Cela signifie qu'un utilisateur pourrait soumettre une requête incluant « IsAdmin », ce qui remplacerait la valeur actuelle et ferait de l'utilisateur un administrateur, comme ceci :

<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>  

Stratégies d'atténuation

Voici quelques stratégies d'atténuation à prendre en compte pour éviter les vulnérabilités liées au Mass Assignment.

Évitez de réutiliser les modèles de données pour les modèles de requête

Il est important de séparer vos modèles de données (qui peuvent être persistés dans une base de données) des modèles utilisés lors de la communication avec un client. La gestion d'une soumission de formulaire vers un contrôleur est une préoccupation très différente de la persistance des données dans une base de données et de leur représentation au sein de celle-ci. Cela crée un niveau de couplage entre le frontend et la couche de persistance bien plus élevé qu'il ne le faudrait.

Soyez explicite dans vos mappages

Le problème avec la liaison (ou le mappage) automatique est que l'absence de mappages explicites facilite l'exposition de propriétés qui ne sont pas censées être accessibles sur le modèle. En étant explicite dans les mappages entre les modèles de requête et le reste de votre backend, vous pouvez prévenir ces types d'expositions dès le départ.

Cela peut être réalisé en utilisant des modèles différents pour les requêtes et les données. Cela ne vous empêche pas d'utiliser un mappeur automatique entre la requête et le modèle de données, car votre modèle de requête ne doit pas exposer de propriétés non autorisées pour la requête spécifique.

Autres exemples

Vous trouverez ci-dessous quelques exemples supplémentaires, dans différents langages, illustrant à quoi cela peut ressembler.

C# - non sécurisé

public class User {
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PasswordHash { get; set; }
public string Country { get; set; }
public string Role { get; set; }
}

[HttpPost]
public ViewResult Edit( User user)
{
// Enregistre simplement l'utilisateur tel quel
UserService.UpdateUser(user);
return Ok();
}

C# - sécurisé

public class User {
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PasswordHash { get; set; }
public string Country { get; set; }
public string Role { get; set; }
}
public class UpdateUserViewModel {

public string FirstName { get; set; }
public string LastName { get; set; }
public string Country { get; set; }
}

[HttpPost]
public ViewResult Edit(UpdateUserViewModel userModel)
{
var user = Request.User;

user.FirstName = userModel.FirstName;
user.LastName = userModel.LastName;
user.Country = userModel.Country;

UserService.UpdateUser(user);

return Ok();
}

C# - alternative - exclusion de paramètres

public class User {
public long Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string PasswordHash { get; set; }
public string Country { get; set; }
public string Role { get; set; }
}

[HttpPost]
public ViewResult Edit([Bind(Include = "FirstName,LastName,Country")] User user)
{
if(Request.User.Id != user.Id) {
return Forbidden("Tentative de modification d'un autre utilisateur");
}
var existingUser = Request.User;
user.PasswordHash = existingUser.PasswordHash;
user.Role = existingUser.Role;

UserService.UpdateUser(user);

return Ok();
}

Java - non sécurisé

public class User {
public int id;
public String firstName;
public String lastName;
public String passwordHash;
public String country;
public String role;
}

@RequestMapping(value = "/updateUser", method = RequestMethod.POST)
public String updateUser(User user) {
userService.update(user);
return "userUpdatedPage";
}

‍
Java - sécurisé

public class UserViewModel {
public String firstName;
public String lastName;
public String country;
}
public class User {
public int id;
public String firstName;
public String lastName;
public String passwordHash;
public String country;
public String role;
}
@RequestMapping(value = "/updateUser", method = RequestMethod.POST)
public String updateUser(@AuthenticationPrincipal User currentUser, UserViewModel userViewModel) {
currentUser.firstName = userViewModel.firstName;
currentUser.lastName = userViewModel.lastName;
currentUser.country = userViewModel.country;

userService.update(currentUser);
return "userUpdatedPage";
}

Javascript - non sécurisé

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

Object.assign(user, req.body);

UserService.Update(user);

return "L'utilisateur a été mis à jour";
})

Javascript - sécurisé

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(user);

return "L'utilisateur a été mis à jour";
})

Python - Non sécurisé

@app.route("/user/update", methods=['POST'])
def update_user():

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

for key, value in form.items():
setattr(user, key, value)

UserService.UpdateUser(user)

return redirect("/user", code=201)

Python - Sécurisé

@app.route("/user/update", methods=['POST'])
def update_user():

user = request.user
form = request.form

user.firstName = form.firstName
user.lastName = form.lastName

UserService.UpdateUser(user)
return redirect("/user", code=201)
Slogan

Le codage sécurisé en pratique

Explorez nos directives de développement sécurisé pour comprendre et atténuer les vulnérabilités logicielles telles que l'OWASP Top 10, et plongez dans des missions de formation guidées pour vous exercer sur des simulations d'applications réelles.

Autres directives
Réserver une démo