2011-09-25 12 views
5

Voglio testare se un percorso dato dall'utente andare giù come:Regex per verificare se un percorso solo scendere

my/down/path

al contrario di:

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

per motivi di sicurezza.

Ho già fatto questo:

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

Ma l'utente dovrebbe essere consentito di utilizzare l''.' nel suo percorso (come: my/./path, che non è utile ma lui può) e non so come considerare esso.

Sto quindi cercando una regex sicura per verificare questo.

Grazie

EDIT: Dopo aver visto le risposte, sì sarebbe bene se il controllo di prova se il percorso vero e proprio (rimozione '.' e '..') è un percorso verso il basso.

+0

Ho aggiunto il tag [sicurezza] che dovrebbe ottenere la domanda più attenzione e, si spera, forse da qualcuno che conosce una buona libreria di attraversamento del percorso PHP. Come suggerisce la mia risposta, questo è un problema non banale. –

risposta

2

Probabilmente non si desidera controllare che un percorso non contenga .. ma si desideri invece controllare che, se valutato come intero, non salga. Per esempio. ./path/.. è ancora in ., anche se contiene ...

È possibile trovare un'implementazione di 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; 

Ramoscello non utilizzare realpath per la validazione, perché realpath ha problemi con i percorsi in archivio Phar. Inoltre, realpath funziona solo se il percorso esiste già.

+0

Ho aggiunto una risposta che distingue la convalida della profondità del percorso dalla convalida del traversamento del percorso. Nulla di personale contro la tua risposta, ma poiché OP ha menzionato la sicurezza, volevo avere almeno una risposta che si rivolgesse ad ambienti più ostili. –

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

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

usando '..' potrebbe comunque dare come risultato un percorso sotto il percorso consentito, ad es. quando '/ only/data/below/here' quindi'/only/data/below/here/foo/.. 'sarebbe ancora okay. – Gordon

+0

@Gordon: come andresti in basso senza usare '..'? – PeeHaa

+0

@PeeHaa, immagino: '/ percorso/a/dir /../../ a/dir/samefile' –

4

si può semplicemente verificare se la realpath del percorso utente fornito inizia con il percorso consentito:

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

Demo on codepad

Si noti che questo sarà anche tornare false per le directory che non esistono sotto $allowedPath.

+2

A seconda di cosa esattamente ti serve per 'realpath' potrebbe non essere applicabile. 'realpath' richiede che esista il percorso, ovvero non è possibile utilizzare questo controllo per la creazione di nuovi percorsi. Inoltre 'realpath' fallirà per alcuni casi limite come gli archivi Phar. – NikiC

0

Se si desidera solo impedire all'utente di salire nella gerarchia percorso, è possibile cercare in modo esplicito '..':

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

che è anche più veloce di esplodere e in_array.

Benchmarking:

<?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

punto di riferimento inutile è inutile :) – Gordon

0

Le risposte precedenti (inclusa quella accettata) profondità del percorso indirizzo ma non percorso trasversale. Poiché la domanda specificava esplicitamente che questo è per la sicurezza, il controllo casuale come quello descritto finora potrebbe non essere sufficiente.

Per esempio,

  • vi preoccupate per l'attraversamento tramite link hard o soft sotto la directory di lavoro corrente?
  • Il sistema su cui ci si trova (o potrebbe potenzialmente distribuire) supporta l'Unicode?
  • Quante cose stanno valutando la stringa in questione prima o dopo che il codice PHP la vede? Il server web? Il guscio? Qualcos'altro?

Supponiamo di inviare una stringa al tuo script come ./..%2f../? È importante per la tua applicazione che questa stringa mi porterà su due livelli? O che gli script forniti in altre risposte non prenderanno questo perché non valutano a ..?

Che dire di ./\.\./? Se il percorso viene analizzato dividendo su entrambi i valori \ e /, lo script nella risposta accettata non lo catturerà perché ogni parte avrà l'aspetto di . che è semplicemente la directory corrente. Ma una tipica shell UNIX tratta lo \ come un carattere di escape, quindi passarlo a ./\.\./ equivale a ./../ e così l'utente malintenzionato può sfruttare il fatto che lo script combina i test per i percorsi di stile UNIX e Windows.

Se per "sicurezza" si intende realmente che si desidera fornire protezione contro errori casuali e errori di battitura, allora le altre risposte sono probabilmente sufficienti. Se si sta programmando per un ambiente ostile e si desidera prevenire violazioni da attacchi intenzionali, si graffiano a malapena la superficie e si consiglia di leggere la programmazione sicura su OWASP. Vorrei iniziare con il loro articles on Path Traversal e poi leggere sugli altri attacchi che hanno delineato e su come evitarli e, cosa più importante, come testarli.

+0

Vorrei vedere se questo si applica a un percorso che è memorizzato in una stringa PHP e viene passato a una delle funzioni di gestione del file system PHP.Più precisamente: dubito sinceramente che se passo una stringa a una funzione di FS in PHP eseguirà la decodifica dell'URL o interpreterà i backslash come sequenze di escape. Ma mi piacerebbe essere smentito, in quanto ciò avrebbe gravi implicazioni sulla sicurezza in molte biblioteche che utilizzano un approccio come quello che ho postato sopra. – NikiC

+0

Hai seguito il link che ho fornito? L'esempio di OWASP è scritto in PHP. Ho anche notato nella mia risposta che il comportamento esatto dipende da cosa elabora l'URL prima * e * dopo che PHP ha ottenuto la stringa. Non mi sorprende affatto se sono interessate molte librerie comuni dal momento che il # 8 dei rischi di sicurezza delle applicazioni Web di Top 10 OWASP è un errore di limitazione dell'accesso agli URL, di cui il traversal del percorso è uno dei vettori principali. Questo non renderebbe OWASP Top 10 se le pratiche prevalenti fossero solide. –

+0

Sì, voglio rendere la mia app davvero sicura, sto lavorando in locale sotto Windows ma potrebbe essere su linux remotly. Non capisco tutto quello che hai detto, l'utente pubblica un modulo, ottengo i valori, controllo il campo e quindi, ad esempio, mostra il valore del campo corrispondente al file. Presumo che readfile (o qualsiasi funzione PHP) interpreti stringhe arg come qualsiasi altra stringa in codice php come un test "$ str == '..'". Per la stringa divisa './\./', ho già creato una funzione che controlla la costante DIRECTORY_SEPARATOR e sostituisce/o \ al loro opposto. Pensavo che avesse risolto il problema. – Leto

Problemi correlati