
Download of files
Il est très courant que les applications aient besoin, à un moment ou à un autre, de permettre aux utilisateurs de télécharger un fichier (pour l'utiliser ou simplement pour le stockage) quelque part dans l'application. Bien que cela semble assez simple, la manière dont cette fonction est implémentée peut être assez critique en raison des risques potentiels associés à la gestion des téléchargements de fichiers.
Jetez un œil à cet exemple rapide, juste pour vous donner une meilleure compréhension visuelle de ce que nous voulons dire.
Disons qu'il s'agit d'une application qui permet aux utilisateurs de télécharger une photo de profil :
chaîne publique UploadProfilePicture (FormFile UploadedFile)
{
//Génère le chemin pour enregistrer le fichier téléchargé dans
var path = $ ». /uploads/avatars/ {request.user.id}/{uploadedFile.FileName} » ;
//Sauvegarde du fichier
var LocalFile = File.OpenWrite (chemin) ;
LocalFile.write (UploadedFile.readToEnd ()) ;
Fichier local .Flush () ;
LocalFile.close () ;
//Mise à jour de la photo de profil
UserProfile.UpdateUserProfilePicture (request.User, chemin)
chemin de retour ;
}
Il s'agirait d'une fonction de téléchargement très basique qui s'avère également vulnérable à Path Traversal.
En fonction de l'implémentation exacte de l'application, un attaquant pourrait télécharger une autre page/un autre script (pensez à des fichiers .asp, .aspx ou .php) qui permettrait d'appeler directement et d'exécuter du code arbitraire. Cela pourrait également permettre de remplacer les fichiers existants.
Problème 1 : enregistrement sur un disque local plutôt que sur un magasin de données externe
À mesure que l'utilisation des services cloud se généralise, les applications sont fournies dans des conteneurs, les configurations de haute disponibilité sont devenues la norme et la pratique consistant à écrire des fichiers téléchargés sur le disque local de l'application doit être évitée à tout prix.
Les fichiers doivent être téléchargés dans une forme de stockage central dans la mesure du possible (stockage par blocs ou base de données). Cela permet d'éviter des catégories entières de failles de sécurité dans ce cas.
Problème 2 : les extensions ne sont pas validées
Dans de nombreux cas, lorsqu'une vulnérabilité de téléchargement de fichiers est exploitée, elle repose sur la possibilité de télécharger un fichier avec une extension spécifique. Il est donc vivement conseillé d'utiliser une « liste d'extensions autorisées » pour les fichiers qui peuvent être téléchargés.
Assurez-vous d'utiliser les méthodes fournies par votre langage/framework pour obtenir l'extension du fichier afin d'éviter des problèmes, comme l'injection d'octets nuls.
Il peut également être tentant de valider le type de contenu du téléchargement, mais cela peut le rendre très fragile, étant donné que les types de contenu utilisés pour des fichiers spécifiques peuvent différer d'un système d'exploitation à l'autre. Il ne vous dit rien non plus sur le fichier lui-même puisque le type de contenu est purement un mappage provenant d'une extension.
Problème 3 : ne pas empêcher la traversée du chemin
Un autre problème courant lié aux téléchargements de fichiers est qu'ils ont également tendance à être vulnérables à la traversée de chemins. C'est un tout en soi, donc plutôt que d'essayer de le résumer ici, donnez la directive complète sur Traversée de chemin un coup d'œil.
Plus d'exemples
Vous trouverez ci-dessous quelques exemples supplémentaires de téléchargements de fichiers sécurisés et non sécurisés que vous pouvez consulter.
C# - 非安全
chaîne publique UploadProfilePicture (IFormFile UploadedFile)
{
//Génère le chemin pour enregistrer le fichier téléchargé dans
var path = $ ». /uploads/avatars/ {request.user.id}/{uploadedFile.FileName} » ;
//Sauvegarde du fichier
var LocalFile = File.OpenWrite (chemin) ;
LocalFile.write (UploadedFile.readToEnd ()) ;
Fichier local .Flush () ;
LocalFile.close () ;
//Mise à jour de la photo de profil
UserProfile.UpdateUserProfilePicture (request.User, chemin)
chemin de retour ;
}
C# - 安全
liste publique <string>AllowedExtensions = new () {« .png », « .jpg », « .gif"} ;
chaîne publique UploadProfilePicture (IFormFile UploadedFile)
{
//REMARQUE : La meilleure option est d'éviter d'enregistrer des fichiers sur le disque local.
var basePath = path.getFullPath (». /uploads/avatars/ «) ;
//Empêche la traversée des chemins en n'utilisant pas le nom de fichier fourni. Également nécessaire pour éviter les conflits de noms de fichiers.
var NewFileName = GenerateFileName (UploadedFile.FileName) ;
//Génère le chemin pour enregistrer le fichier téléchargé dans
var CanonicalPath = Path.Combine (BasePath, NewFileName) ;
//Assurez-vous que nous n'avons pas accidentellement enregistré dans un dossier en dehors du dossier de base
si (! Chemin canonique. Commence par (BasePath)
{
return BadRequest (« Tentative d'enregistrement du fichier en dehors du dossier de téléchargement ») ;
}
//Assurez-vous que seules les extensions autorisées sont enregistrées
si (! Extension de fichier autorisée (Extensions autorisées téléchargées)
{
return BadRequest (« L'extension n'est pas autorisée ») ;
}
//Sauvegarde du fichier
var LocalFile = File.OpenWrite (CanonicalPath) ;
LocalFile.write (UploadedFile.readToEnd ()) ;
Fichier local .Flush () ;
LocalFile.close () ;
//Mise à jour de la photo de profil
Profil utilisateur. Mettre à jour l'image du profil utilisateur (request.User, CanonicalPath)
chemin de retour ;
public bool generateFileName (chaîne OriginalFileName) {
return $ "{guid.newGUID ()} {path.getExtension (OriginalFileName)} » ;
}
<string>public bool IsFileAllowedExtension (chaîne FileName, liste des extensions) {
renvoie Extensions.contains (Path.getExtension (FileName)) ;
}