SÉCURITÉ
Web, sécurité et Laravel : exemples et cas d'usage [3/3]
Par Aubin Puyoou, publié le 03/10/2021 à 23:04.
Insecure Direct Object Reference (IDOR)
Modification d'identifiants objets vers des ressources non autorisées
Si l'accès à certaines de vos ressources doit être restreint ou limité, il est nécessaire de manipuler vos objets en régulant celui-ci. Ceci peut paraître évident, mais il arrive facilement d'oublier ce type de contrôles dans les phases de développement de l'application. Ainsi suivant les profils, rôles et usages de vos utilisateurs, il devient obligatoire de placer des limites.
Prenons la requête HTTP suivante : http://exemple.com?user=1
. Nous devinerions logiquement que la ressource
à laquelle l'utilisateur souhaite accéder est l'utilisateur d'identifiant 1. S'il est possible de remplacer cet identifiant
par un 2 http://exemple.com?user=2
, de manière à obtenir la ressource associée, sous un compte utilisateur non
autorisé, alors l'application serait potentiellement vulnérable aux IDOR.
Exemples d'usages de failles IDOR
Il existe un lot d'exemples d'utilisation de ces failles. Premier exemple, le site Vimeo qui permettait
via son formulaire de réinitialisation de mot de passe, de modifier l'identifiant utilisateur
auquel il était assigné :
https://vimeo.com/forgot_password/[user_id]/[token]
Seule était vérifiée la validité du token, mais pas son appartenance, l'identifiant référençant directement
l'utilisateur auquel attribuer le mot de passe saisi.
Autre exemple, le site Rockstar Games permettait à un utilisateur de manipuler l'identifiant utilisateur associé à un nouveau commentaire. Une série d'autres exemples sont disponibles sur le lien suivant : Top 25 IDOR Bug Bounties Reports.
Laravel Gates & Policies
Un moyen facile de se protéger de ce type de faille et de vérifier à chaque requête l'authenticité de l'utilisateur.
Laravel propose une facade "Gate" permettant de définir de simples fonctions retournant un booléen,
déterminant ou non l'autorisation d'accès à une ressource. Dans le cas où l'utilisateur souhaiterait
accéder à des images ayant un accès régulé par exemple, il suffirait simplement de vérifier son appartenance, en
ajoutant cette closure dans la méthode boot()
de notre AuthServiceProvider
.
Gate::define('access-image', function ($user, $image){
return $user->id === $image->user_id;
});
Ainsi toutes les routes associées à un middleware('can:access-image')
,
ne respectant par la règle fixée dans la Closure seraient bloquées et nos objets protégés. Il est également possible
de lier des méthodes d'authentification plus complexes à nos contrôleurs par le biais de Policies. Ainsi, une ImagePolicy
serait associée à un ImageController, de façon à pouvoir lier un ensemble de méthodes de vérification
à celles de bases du contrôleur :
Contrôleur | Policy |
---|---|
index | viewAny |
show | view |
create | create |
store | create |
edit | update |
update | update |
destroy | delete |
De manière identique à nos Gates, ces méthodes sont directement associables à nos routes via la
directive middleware(can:view,update,...)
, et protègent donc l'accès au contrôleur.
La classe serait ainsi définie :
class ImagePolicy
{
public function update(User $user, Image $image)
{
// return update policy rule;
}
public function view(User $user, Image $image)
{
// return view policy rule;
}
...
}
Bien évidemment, il est également possible de limiter les accès aux ressources dans nos requêtes SQL. Le principe ici reste de contrôler l'accès aux enregistrements de notre base.
Exécution de code à distance à via la désérialisation de Cookie
Processus de sérialisation
Une des principes fondamentaux de la transmission d'informations entre plusieurs machines est la sérialisation. Le processus est simple, un objet sous une forme A est transformé en un ensemble de données uniformes, généralement une chaîne de caractères de forme B, mais conservant l'ensemble de ses propriétés. Cette chaîne suit à un modèle type, de manière à pouvoir facilement être réinterprété par un autre serveur. Cette problématique est la source de l'invention des formats XML et JSON. Ce format peut ensuite être transféré vers une autre machine capable d'interpréter le format, de façon à le retransformer en objet dans nos processus.
Sérialisation en PHP
Les méthodes de sérialisation par défaut de PHP permettent d'encoder les données objets dans un format particulier.
Prenons une classe Utilisateur
ayant pour modèle :
class Utilisateur
{
private $nom;
private $role;
....
}
Ici, une instance d'objet sérialisé après un appel à la méthode serialize()
de PHP serait
représenté sous la forme suivante : "O:11:"Utilisateur":2:{s:17:"\0Utilisateur\0nom";s:5:"alice";s:17:"\0Utilisateur\0role";s:5:"admin";}"
.
Ainsi, un objet peut être stocké dans un cookie directement sur le navigateur de l'internaute.
Cookies sérialisés de Laravel
Si vous êtes amenés à utiliser les méthodes d'authentification du framework, il faut savoir qu'un ensemble d'objets relatifs à la session en cours est sérialisé puis chiffré avant d'être stocké sous forme de Cookie sur le navigateur des internautes.
laravel_session:eyJpdiI6InByOXJoeGNuVjJ1Qjh3Q09RRDNHK3c9PSIsInZhbHVlIjoiNzc3TWlkeGhXV2x6SGlYVEpnTVEyJ...
Cette donnée est chiffrée avec l'APP_KEY générée à la création du projet. Ainsi, si un utilisateur du site pouvait être en capacité de déchiffrer le cookie, il lui serait possible de modifier des données et donc potentiellement d'insérer du code, menant ainsi à d'autres types de failles, comme de l'exécution de code ou des failles XSS.
Injection de code via désérialisation d'objets
Si lors de la désérialisation de votre objet, un attribut de classe est utilisé dans vos vues,
ou est associé àl'exécution d'une commande, via un shell_exec()
,
un utilisateur malicieux pourrait par exemple afficher des données de configuration, ou
manipuler les fichiers du serveur.
Ainsi, certaines applications développées avec Laravel offraient la possibilité aux utilisateurs de sauvegarder et de réafficher des données chiffrées directement à l'aide de l'APP_KEY. Bien qu'il ne soit pas possible de déchiffrer directement le Cookie, il était possible de chiffrer les données sérialisées correspondantes à celui-ci, et d'injecter dans celui-ci du code qui serait exécuté car détournant le fonctionnement natif du framework. Cette faille a valu un correctif modifiant son moteur d'authentification en 2020, alors qu'il était déjà bien éprouvé.
Ne jamais faire confiance aux saisies utilisateur
La morale ici est donc de toujours filtrer et valider les données que peuvent transmettre les utilisateurs de vos applications. Aussi, si jamais une des fonctionnalités de votre site permet au client de chiffrer des données et de les traiter, veillez à ne pas utiliser les clés propres aux outils dont vous vous servez pour les implémenter.