only - Evitar el recorrido del directorio en PHP pero permitiendo rutas
readdir php (5)
Tengo un camino base / lo que sea / foo /
y $_GET[''path'']
debería ser relativo a él.
Sin embargo, ¿cómo logro esto (leer el directorio), sin permitir el recorrido del directorio?
p.ej.
//./.|/././
No se filtrará correctamente
Bueno, una opción sería comparar las rutas reales:
$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!
}
Básicamente, realpath()
resolverá la ruta proporcionada a una ruta física dura real (resolviendo enlaces simbólicos, ..
, .
, /
, //
, etc.) ... Entonces, si la ruta real del usuario no comienza con la ruta base real, está tratando de hacer un cruce. Tenga en cuenta que la salida de realpath
no tendrá ningún "directorio virtual" como .
o ..
...
La respuesta de ircmaxell no era del todo correcta. He visto esa solución en varios fragmentos pero tiene un error relacionado con la salida de realpath()
. La realpath()
elimina el separador de directorio final, así que imagine dos directorios contiguos como:
/foo/bar/baz/
/foo/bar/baz_baz/
Como realpath()
eliminaría el último separador de directorios, su método devolvería "good path" si $_GET[''path'']
fuera igual a "../baz_baz", ya que sería algo así como
strpos("/foo/bar/baz_baz", "/foo/bar/baz")
Tal vez:
$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!
}
No es suficiente verificar patrones como ../ o los me gusta. Tome "../" por ejemplo, qué URI codifica a "% 2e% 2e% 2f". Si su verificación de patrón ocurre antes de una decodificación, se perderá este intento de cruce. Hay otros trucos que los hackers pueden hacer para eludir un verificador de patrones, especialmente cuando usan cadenas codificadas.
He tenido el mayor éxito deteniendo esto canonicalizando cualquier cadena de ruta a su ruta absoluta utilizando algo como realpath () como sugiere ircmaxwell. Solo entonces empiezo a buscar ataques cruzados al compararlos con una ruta base que he predefinido.
Puede que tengas la tentación de probar y usar regex para eliminar todos los ../s, pero hay algunas funciones agradables integradas en PHP que harán un trabajo mucho mejor:
$ page = nombre base (realpath ($ _ GET));
nombre base: elimina toda la información de directorio de la ruta, por ejemplo, ../pages/about.php se convertiría en about.php
realpath: devuelve una ruta completa al archivo, por ejemplo, about.php se convertiría en /home/www/pages/about.php, pero solo si el archivo existe.
Combinados devuelven solo el nombre de los archivos, pero solo si el archivo existe.
Supongo que te refieres sin permitir a los usuarios atravesar el directorio sí?
Si está tratando de evitar que su propio PHP atraviese el directorio, debe hacer que el php funcione correctamente en primer lugar.
Lo que necesita para detener a los usuarios es un archivo .htaccess modificado ...
Options -Indexes
(Todo esto supone que estás hablando de usuarios)