2009-12-16 32 views
24

Saludos, Espero que mi pequeño programa sea seguro para que los posibles usuarios malintencionados no puedan ver los archivos confidenciales en el servidor.Desinfectar la ruta del archivo en PHP

$path = "/home/gsmcms/public_html/central/app/webroot/{$_GET['file']}"; 


    if(file_exists($path)) { 
     echo file_get_contents($path); 
    } else { 
     header('HTTP/1.1 404 Not Found'); 
    } 

De la parte superior de mi cabeza Sé que de entrada como '../../../../../../etc/passwd' habría problemas, pero preguntándose qué otra entradas malosas que debería esperar y cómo prevenirlas.

+1

Sólo excluye ninguna entrada que contiene ../ – halfdan

+2

acordó que resolver un problema, pero estoy asumiendo que hay muchos otros peligros que tengo que mirar hacia fuera. Estoy buscando una buena solución revestida de hierro para todos ellos – SeanDowney

+3

@halfdan - siempre trate de evitar un enfoque de lista negra para la seguridad como esta, siempre habrá algo que extrañará. Como el uso de caracteres de retroceso, pestañas, líneas nuevas, caracteres nulos, otros caracteres Unicode, o caracteres Unicode rotos intencionalmente que pasarían su filtro, pero aún causan que la función PHP haga algo que usted estaba tratando de proteger. Pruebe lo que realmente quiere: que la ruta resultante esté en un lugar seguro. – Cheekysoft

Respuesta

33

realpath() le permitirá convertir cualquier ruta que pueda contener información relativa en una ruta absoluta ... luego puede asegurarse de que la ruta esté bajo un determinado subdirectorio del que desea permitir descargas.

+4

Esta fue mi solución final: $ baseDir = "/ home/gsmcms/public_html/central/app/webroot/"; $ path = realpath ($ baseDir. $ _GET ['file']); // si baseDir no está en la parte frontal 0 == strpos, lo más probable es que intente hackear if (strpos ($ path, $ baseDir)) { \t morir ('Ruta inválida'); } elseif (file_exists ($ path)) { \t echo file_get_contents ($ path); } else { \t encabezado ('HTTP/1.1 404 no encontrado '); \t echo "No se pudo encontrar el archivo solicitado"; } – SeanDowney

+1

@SeanDowney hay un error en su solución, es decir, usted debe comprobar que strpos no vuelve falsa o que no sea cero, lo que no lo está haciendo. – Michael

+1

realpath() es un buen amigo pero no lo suficiente; solo devuelve la ruta completa si se refiere a una ruta existente. De lo contrario, devolvería falso; en algunos casos esto no causará ningún problema, pero considerando una operación de "guardar como" con creación automática del subdirectorio, puede arruinar el juego. (El usuario le dice dónde colocar el archivo pero usted responde "ruta inválida" ya que realpath devuelve falso.) Probablemente no sea lo que está haciendo; solo pensé mencionarlo – dkellner

11

Use basename en lugar de tratar de anticipar todas las rutas inseguras que un usuario podría proporcionar.

+0

esto puede funcionar en algunas situaciones, sin embargo, también estoy esperando incluir directorios, por ejemplo: '/js/jquery/jquery.js' – SeanDowney

6

Si puede, use una lista blanca como una matriz de archivos permitidos y verifique la entrada con eso: si el archivo solicitado por el usuario no está en esa lista, denegar la solicitud.

+0

Ésta sería la mejor idea, pero probablemente más trabajo de lo que quiero hacer :) – SeanDowney

+0

A menos que quiera filtrar la fuente de todos sus archivos bajo su Webroot, es probable que desee hacer esto. – Cheekysoft

4

Aquí hay un riesgo de seguridad adicional y significativo. Esta secuencia de comandos inyectará la fuente de un archivo en la secuencia de salida sin ningún procesamiento del lado del servidor. Esto significa que todo su código fuente de cualquier archivo accesible se filtrará a internet.

+0

buen punto, voy a añadir una lista blanca de extensiones permitidas, tales como: js, css, jpg, gif ... – SeanDowney

7

solución por la OP:

$baseDir = "/home/gsmcms/public_html/central/app/webroot/"; 
$path = realpath($baseDir . $_GET['file']); 

// if baseDir isn't at the front 0==strpos, most likely hacking attempt 
if(strpos($path, $baseDir) !== 0 || strpos($path, $baseDir) === false) { 
    die('Invalid Path'); 
} elseif(file_exists($path)) { 
    echo file_get_contents($path); 
} else { 
    header('HTTP/1.1 404 Not Found'); 
    echo "The requested file could not be found"; 
} 
+0

favor aprenden a utilizar las funciones avanzadas de SO! :) simplifica * copiar y pegar * –

+0

FYI, encontré un error en esta solución, pero mi edición que contiene la corrección fue rechazada. – Michael

+0

Por favor, consulte lo anterior. Debería ser más explícito y justificado para el propósito de cordura. –

1

Incluso si está utilizando la ruta real, aún debe despojar a todos ".." antes de usarlo. De lo contrario, un atacante puede leer la estructura de directorios completa de los servidores con la fuerza bruta, p. "valid_folder /../../ test_if_this_folder_name_exists/valid_folder": si la aplicación acepta esta ruta, el atacante sabe que la carpeta existe.

0

Para quitar todo /. /.. o \. \ .. y convertir a toda la barra inclinada porque los diferentes entornos aceptarán barras diagonales. Esto debería proporcionar un filtro bastante seguro para la entrada de ruta. En su código, debería compararlo con los directorios principales a los que no quiere acceder por las dudas.

$path = realpath(implode('/', array_map(function($value) {return trim($value, '.');}, explode('/', str_replace('\\', '/', $path))))); 
+0

@Koby: ¿Por qué editaste mi respuesta? El str_replace pone todas las barras diagonales y el realpath hace que sea una sola barra. No edite mi código –

Cuestiones relacionadas