Envoi de fichiers malicieux

Voir la vidéo
Description Sommaire

Lorsque l'on crée une application web il est souvent nécessaire de mettre en place des systèmes permettant d'envoyer des fichiers sur le serveur (document, images...). Ces système d'envoi de fichier sont un vecteur d'attaque important et l'impact peut être désastreux.

Le principe

Si on laisse l'utilisateur envoyer n'importe quel type de fichier il peut envoyer un fichier qu'il pourra ensuite exécuter pour gagner en privilège (lire des fichiers sensible, modifier des fichiers...). PHP est particulièrement sensible à ce genre d'attaque car il est possible d'exécuter un script par une simple URL.

Prenons un cas concret, l'utilisateur envoie un fichier hack.php à la place de son avatar.

if (!empty($_FILES)) {
    $extension = pathinfo($_FILES['avatar']['name'], PATHINFO_EXTENSION);
    $avatarPath = __DIR__ . '/images/' . $userID . '.' . $extension
    move_uploaded_file($_FILES['avatar']['tmp_name'], $avatarPath);
    // ...
}

Le fichier est alors déplacé dans un dossier images avec son extension original. Si maintenant je me rend sur l'URL de mon avatar /images/{ID}.php le code à l'intérieur de mon fichier PHP sera exécuté. On peut par exemple déplacer un fichier qui permettra de manipuler le système de fichier pour lire le code source de l'application.

La solution

Il y a 2 axes de défense contre ce type d'attaque :

  • Meilleur contrôle des fichiers que l'on peut téléverser dans l'application.
  • Limiter l'exécution de fichiers à certains dossiers.

Contrôler les fichiers

Lorsque l'on autorise l'utilisateur à envoyer des images ou des documents, il est nécessaire d'effectuer des contrôles lors de l'envoi de fichier.

$allowedTypes = ["image/jpeg", "image/png"];
$allowedExtensions = ['png', 'jpg', 'jpeg'];

$finfo = finfo_open(FILEINFO_MIME_TYPE); 
$mime = finfo_file($finfo, $_FILES["avatar"]["tmp_name"]);
$extension = strtolower(pathinfo($_FILES["avatar"]['name'], PATHINFO_EXTENSION));

if (
    !in_array($mime, $allowedTypes) || 
    !in_array($extension, $allowedExtensions) 
) {
    throw new \Exception('Type de fichier non accepté');
}

// On peut continuer

Attention, on ne se contentera pas de vérifier le type MIME d'un fichier car il est possible de le fausser. Si on conserve l'extension originale on la validera aussi.

Limiter l'exécution de script

Un autre axe de défense consiste à limiter l'exécution de scripts depuis une URL particulière.

Dans le cas d'Apache on peut ajouter un fichier .htaccess dans un dossier dans lequel on n'attend pas de scripts exécutable.

<FilesMatch "\.php.*$">
  Order allow,deny
  Deny from all
</FilesMatch>

Pour nginx on peut ajouter une règle directement au niveau de la configuration.

server {
    # ...
    location /images {
        location ~* \.php {
            deny all;
        }
    }
    # ...
}
Publié
Technologies utilisées
Auteur :
Grafikart
Partager