2011-09-25 4 views
5

Quiero probar si una trayectoria determinada por el usuario vaya abajo como:expresión regular para comprobar si hay una ruta de ir solamente abajo

my/down/path

al contrario de:

this/path/../../go/up

por razones de seguridad.

ya he hecho esto:

return (bool)preg_match('#^([a-z0-9_-])+(\/[a-z0-9_-])*$#i', $fieldValue); 

Sin embargo, el usuario debe ser permitido el uso de la '.' en su camino (como: my/./path, que no es útil, sino que puede) y no sé cómo considerar eso.

Estoy buscando una expresión regular segura para verificar esto.

Gracias

EDIT: Después de ver las respuestas, sí que estaría bien si la verificación de la prueba si el camino real (eliminando '.' y '..') es un camino hacia abajo.

+0

Agregué la etiqueta [de seguridad] que debería llamar más la atención de la pregunta y, con suerte, quizás de alguien que conozca una buena biblioteca de recorrido transversal de PHP. Como mi respuesta sugiere, este es un problema no trivial. –

Respuesta

2

Probablemente no desee comprobar que una ruta no contiene .., pero en su lugar desea comprobar que si se evalúa como un todo, no sube. P.ej. ./path/.. todavía está en ., aunque contiene ...

puede encontrar una implementación de path depth validation in Twig:

$parts = preg_split('#[\\\\/]+#', $name); 
$level = 0; 
foreach ($parts as $part) { 
    if ('..' === $part) { 
     --$level; 
    } elseif ('.' !== $part) { 
     ++$level; 
    } 

    if ($level < 0) { 
     return false; 
    } 
} 

return true; 

Twig no utilizar realpath para la validación, porque realpath tiene problemas con las rutas de los archivos Phar. Además, realpath solo funciona si la ruta de acceso ya existe.

+0

Agregué una respuesta que distingue la validación de profundidad de ruta de la validación de recorrido transversal. No hay nada personal en contra de su respuesta, pero como OP mencionó la seguridad, quería tener al menos una respuesta que abordara entornos más hostiles. –

0
$folders = $explode('/', $path); 

if (in_array('..', $folders)) { 
    print('Error: path contains ..'); 
} 
+0

utilizando '..' aún podría dar como resultado una ruta por debajo de la ruta permitida, por ejemplo, cuando '/ only/data/below/here' then'/only/data/below/here/foo/.. 'estaría bien. – Gordon

+0

@Gordon: ¿cómo irías abajo sin usar '..'? – PeeHaa

+0

@PeeHaa, supongo: '/ path/to/dir /../../ a/dir/samefile' –

4

puede simplemente comprobar si la ruta real de la ruta proporcionada por el usuario comienza con la ruta permitido:

function isBelowAllowedPath($allowedPath, $pathToCheck) 
{ 
    return strpos(
     realpath($allowedPath . DIRECTORY_SEPARATOR . $pathToCheck), 
     realpath($allowedPath) 
    ) === 0; 
} 

Demo on codepad

Tenga en cuenta que esto también regresará false para los directorios que no existen debajo de $allowedPath.

+2

Dependiendo de lo que necesite exactamente para 'realpath' podría no ser aplicable. 'realpath' requiere que la ruta de acceso exista, es decir, no puede usar esta comprobación para la creación de nuevas rutas. Además, 'realpath' fallará en algunos casos extremos, como los archivos Phar. – NikiC

0

Si sólo desea restringir al usuario a subir en la jerarquía de ruta, puede buscar explícitamente '..':

if (1 === preg_match('/\.\./', $path)) { 
    /* path contains .. */ 
} 

Lo que también es más rápido que explotar y in_array.

Análisis comparativo:

<?php 

$attempts = 100000; 
$path  = 'my/path/with/../invalid'; 

$t = microtime(true); 
for ($i = 0; $i < $attempts; ++$i) { 
    $folders = explode('/', $path); 
    if (in_array('..', $folders)) { 
    /* .. in path */ ; 
    } 
} 
$end = microtime(true); 
printf("in_array: %f\n", $end - $t); 

$t = microtime(true); 
for ($i = 0; $i < $attempts; ++$i) { 
    if (1 === preg_match('/\.\./', $path)) { 
    /* .. in path */ ; 
    } 
} 
$end = microtime(true); 
printf("preg_match: %f\n ", $end - $t); 

in_array: 0.088750

preg_match: 0.071547

+0

benchmark sin sentido no tiene sentido :) – Gordon

0

Las respuestas anteriores (incluida la aceptada) abordan la profundidad de la ruta pero no atraviesan la ruta. Dado que la pregunta específicamente menciona que esto es por seguridad, entonces la verificación casual como la descrita hasta ahora puede no ser suficiente.

Por ejemplo,

  • Le gustan los atraviesa a través de enlaces duros o blandos por debajo del directorio de trabajo actual?
  • ¿El sistema en el que se encuentra (o podría implementarlo) es compatible con Unicode?
  • ¿Cuántas cosas están evaluando la cadena en cuestión antes o después de que lo vea su código PHP? El servidor web? ¿La cáscara? ¿Algo más?

Supongamos que le envío una secuencia de comandos como ./..%2f../? ¿Es importante para su aplicación que esta cadena me lleve dos niveles? ¿O que las secuencias de comandos proporcionadas en otras respuestas no captarán esto porque no evalúa a ..?

¿Qué hay de ./\.\./? Si la ruta se analiza dividiendo en \ y /, la secuencia de comandos en la respuesta aceptada no lo detectará porque cada parte se verá como ., que es simplemente el directorio actual. Pero un shell UNIX típico trata el \ como un carácter de escape, por lo que pasarlo ./\.\./ es equivalente a ./../ y así el atacante puede explotar el hecho de que el script combina pruebas para las rutas de estilo de UNIX y Windows.

Si por "seguridad" realmente quiere decir que desea proporcionar protección contra errores casuales y errores tipográficos, entonces las otras respuestas son probablemente suficientes. Si está programando para un entorno hostil y desea evitar infracciones debidas a ataques deliberados, apenas saltan a la superficie y le aconsejamos que lea la programación segura en OWASP. Comenzaría con su articles on Path Traversal y luego leería sobre los otros ataques que describen, así como sobre cómo evitarlos y, lo que es más importante, cómo probarlos.

+0

Me gustaría ver una referencia sobre si esto se aplica a una ruta que se almacena en una cadena de PHP y se pasa a una de las funciones de manejo del sistema de archivos PHP.Más precisamente: sinceramente dudo que si paso una cadena a una función FS en PHP, haga una decodificación URL o interpretará las barras invertidas como secuencias de escape. Pero me encantaría que se demuestre que estoy equivocado, ya que esto tendría graves implicaciones de seguridad en muchas bibliotecas con un enfoque como el que publiqué anteriormente. – NikiC

+0

¿Has seguido el enlace que proporcioné? El ejemplo de OWASP está escrito en PHP. También señalé específicamente en mi respuesta que el comportamiento exacto depende de qué procesos la URL antes * y * después de PHP obtiene la cadena. No me sorprendería en absoluto si muchas bibliotecas comunes se ven afectadas ya que el n. ° 8 de los 10 principales riesgos de seguridad de aplicaciones web de OWASP es la imposibilidad de restringir el acceso a URL, cuyo recorrido transversal es uno de los principales vectores. Esto no convertiría a OWASP en el Top 10 si las prácticas predominantes fueran sólidas. –

+0

Sí, quiero que mi aplicación sea realmente segura, estoy trabajando en local bajo Windows pero podría estar en Linux remotamente. No entiendo todo lo que dijo, el usuario publica un formulario, obtengo los valores, verifico el campo, y luego, por ejemplo, muestro el valor del campo correspondiente. Supongo que readfile (o cualquier función php) interpreta cadenas arg como cualquier otra cadena en código php como una prueba "$ str == '..' '". Para la cadena dividida './\.\./', ya hice una función que verifica la constante DIRECTORY_SEPARATOR y reemplaza/o \ con su opuesto. Pensé que había resuelto el problema. – Leto

Cuestiones relacionadas