web-dev-qa-db-fra.com

Prévention de la navigation dans les répertoires PHP mais permettant des chemins

J'ai un chemin de base/quoique/foo /

et $_GET['path'] devrait lui être relatif.

Cependant, comment puis-je accomplir cela (lire le répertoire), sans permettre la traversée de répertoire?

par exemple.

/\.\.|\.\./

Ne filtre pas correctement.

30
Johnny

Eh bien, une option serait de comparer les chemins réels:

$basepath = '/foo/bar/baz/';
$realBase = realpath($basepath);

$userpath = $basepath . $_GET['path'];
$realUserPath = realpath($userpath);

if ($realUserPath === false || strpos($realUserPath, $realBase) !== 0) {
    //Directory Traversal!
} else {
    //Good path!
}

En gros, realpath() résoudra le chemin fourni en un chemin physique dur (résolution des liens symboliques, .., ., /, //, etc) ... Donc, si le chemin utilisateur réel ne commence pas par le chemin réel, il essaie de faire une traversée. Notez que la sortie de realpath sera not aura des "répertoires virtuels" tels que . ou .....

105
ircmaxell

la réponse d'ircmaxell n'était pas tout à fait correcte. J'ai vu cette solution dans plusieurs extraits mais il y a un bogue qui est lié à la sortie de realpath(). La fonction realpath() supprime le séparateur de répertoire final, imaginez donc deux répertoires contigus tels que:

/foo/bar/baz/

/foo/bar/baz_baz/

Comme realpath() enlèverait le dernier séparateur de répertoire, votre méthode renverrait "bon chemin" si $_GET['path'] était égal à "../baz_baz", ce qui donnerait quelque chose comme:

strpos("/foo/bar/baz_baz", "/foo/bar/baz")

Peut être:

$basepath = '/foo/bar/baz/';
$realBase = realpath($basepath);

$userpath = $basepath . $_GET['path'];
$realUserPath = realpath($userpath);

if ($realUserPath === false || strcmp($realUserPath, $realBase) !== 0 || strpos($realUserPath, $realBase . DIRECTORY_SEPARATOR) !== 0) {
    //Directory Traversal!
} else {
    //Good path!
}
14

Il ne suffit pas de rechercher des motifs tels que ../ ou les goûts. Prenons "../" par exemple, quel URI code pour "% 2e% 2e% 2f". Si votre vérification de modèle se produit avant un décodage, vous manquerez cette tentative de traversée. Il existe d'autres astuces que les pirates peuvent faire pour contourner un vérificateur de modèle, en particulier lors de l'utilisation de chaînes codées.

J'ai eu le plus de succès en les arrêtant en canonisant n'importe quelle chaîne de chemin en son chemin absolu en utilisant quelque chose comme realpath () comme le suggère ircmaxwell. C'est seulement à ce moment-là que je commence à vérifier les attaques par croisement en les faisant correspondre à un chemin de base que j'ai prédéfini.

4
Cowlby

Vous pouvez être tenté d’utiliser regex pour supprimer tous les ../s, mais il existe quelques fonctions Nice intégrées à PHP qui feront un bien meilleur travail:

$ page = nom de base (realpath ($ _GET));

nom_base - supprime toutes les informations de répertoire du chemin, par exemple. ../pages/about.php deviendrait about.php

realpath - renvoie un chemin complet vers le fichier, par exemple about.php deviendrait /home/www/pages/about.php, mais seulement si le fichier existe.

Combinés, ils ne renvoient que le nom du fichier, mais uniquement si le fichier existe. 

1
L4m0r

Je suppose que vous voulez dire sans permettre à utilisateurs de parcourir le répertoire oui?

Si vous essayez d'empêcher votre propre PHP de parcourir le répertoire, vous devez simplement faire en sorte que le PHP fonctionne correctement.

Ce qu'il vous faut pour arrêter les utilisateurs, c'est un fichier .htaccess modifié ...

Options -Indexes

(Tout cela suppose que vous parlez d'utilisateurs)

0
J V