Codice per la convalida dell'URL di reindirizzamento

2

La mia applicazione web mi consente di inviare il modulo di login per reindirizzare a una determinata pagina del sito Web dopo aver effettuato l'accesso. Ad esempio, se l'utente passa all'url http://localhost/login.php?returnto=%2FconfirmEmail.php , dopo aver completato la procedura di accesso, automaticamente reindirizzato a http://localhost/confirmEmail.php .

Dopo aver completato la procedura di accesso, ho usato il seguente codice per convalidare l'url:

function isValidReturnURL( $input ) {
    if ( !is_string( $input ) ) { //Input is not a string
        return FALSE;
    }
    $cleanString = trim( urldecode( $input ) );
    if ( !strlen( $cleanString ) ) { //Input is an empty string
        return FALSE;
    }
    //Edit: I updated the following code (see below)
    $urlHost = parse_url( $cleanString, PHP_URL_HOST );
    if ( !empty($urlHost) ) { //All local urls will be relative
        return FALSE;
    }
    //End edit
    return TRUE;
}
if ( isValidReturnURL( $_GET["returnto"] ) && $continue ) {
    header("Location:" . urldecode( $_GET["returnto"] ) );
}

Mi piacerebbe sapere se questo codice è sicuro o sarà ancora possibile per un utente malintenzionato causare il reindirizzamento della pagina a un sito Web esterno (o ad altre posizioni dannose)?

Aggiornamento: in precedenza, il mio codice per garantire che i collegamenti fossero locali era il seguente:

...
if ( $cleanString[0] !== '/' ) { //All local urls should start with '/'
    return FALSE;
}
...

Come indicato da vari commenti , questo considererà validi gli URL relativi al protocollo (ad esempio //google.com). Ho quindi aggiornato il codice sopra riportato per tenerlo in considerazione.

    
posta Inkbug 24.08.2016 - 18:23
fonte

5 risposte

1

Invece di convalidare se l'URL è cattivo, basta anteporre il percorso di contesto dell'applicazione.

  1. Verifica che $_GET["returnto"] non contenga ritorni di riga ( \r o \n ), perché non sono sicuro che la funzione header esegua internamente questo controllo di sicurezza.
  2. Scrivi la posizione di reindirizzamento con il percorso di contesto anteposto

    header("Location: https://example.com/" . $_GET["returnto"] );
    

    Quindi, se viene passato http://badguy.net , l'utente verrà reindirizzato in modo sicuro a https://example.com/http://badguy.net , che è una pagina 404 innocua fornita dal tuo sito.

  3. Non è più necessario verificare che gli URL siano relativi.

Nota: ho visto dove hai fatto precedentemente questo errore. Se si urldecode quando si agisce sulla variabile, si dovrebbe anche urldecode quando sanificazione / verifica se è valido. Allo stesso modo, se non esegui urldecode durante la sterilizzazione, non dovresti urldecode quando agisci.

  1. Avviso: assicurati di non avere gestori di reindirizzamento aperti. Ad esempio, alcuni siti utilizzano un URL speciale come https://example.com/go?url=http://google.com in modo che quando un utente fa clic, è possibile accedere quando lo hanno fatto.

    Se disponi di una funzione di questo tipo, devi aggiungere un controllo di sicurezza separato, altrimenti l'utente potrebbe passare go?url=http://badguy.net (che è un URL relativo), accedere al reindirizzamento aperto e quindi l'utente viene reindirizzato a http://badguy.net .

risposta data 24.08.2016 - 20:49
fonte
4

Non è sicuro:

returnto=//google.com

Questo è un URL relativo al protocollo, verrà reindirizzato a link se il tuo sito è https, altrimenti reindirizzerà a link

    
risposta data 24.08.2016 - 18:27
fonte
3

Quindi, perché ho cose migliori da fare, ho appena passato un po 'di tempo a cercare un carattere di spazio bianco che non sia tagliato da trim, e quindi rovina parse_url, ma è uno spazio bianco valido nell'intestazione la funzione.

Ho scoperto che se codifico il carattere unicode formfeed, U + 000C, e lo posto prima dell'URL in questo modo:

%0Chttp%3A%2F%2Fwww.google.com

Il tuo codice continuerà a reindirizzare a Google. Scusate! : D

    
risposta data 24.08.2016 - 20:16
fonte
2

La soluzione convalidata è vulnerabile (vedi altri post).

Ti suggerisco di WHITELIST l'URL di reindirizzamento. Ancora meglio: usa un mapper per una lista bianca.

&something=value&redirectUrl=3

3 mappato nel back-end a un URL piacevole. In questo modo sei sicuro

Se sei l'URL della whitelist, fai attenzione anche ai parametri .. Ecco perché essere sicuri al 100% e avere un bel posto in cui sono memorizzati tutti gli URL (che è facile da mantenere), vorrei solo consentire chiavi specifiche (come il "3" nell'esempio qui sopra) che mapperei per pulire gli URL.

Il punto di questa tecnica è che non dovrai pensare a quali possibili hack potrebbero spezzarti (come hai visto con la soluzione convalidata, una nuova idea potrebbe far scattare e rompere le tue difese). Se applichi questa tecnica, sei sicuro di controllare il reindirizzamento.

L'unico svantaggio (funzionale) è che non puoi reindirizzare a un URL dinamico. Che probabilmente non hai intenzione di fare, dato il tuo caso d'uso:)

La linea di fondo è: avere un URL di reindirizzamento come valore di parametro in un URL è generalmente una cattiva idea, in quanto la convalida è soggetta a errori. Se puoi evitare la convalida del tutto, fallo! Anche se gli standard evolvono (vedi: URL relativi), sei ancora al sicuro.

    
risposta data 25.08.2016 - 13:30
fonte
2

Il modo migliore per correggere una vulnerabilità è evitarlo del tutto.

Considera tokenizzazione o whitelist di ciò che è possibile. Non utilizzare GET / POST per trasferire variabili sensibili! Prendi in considerazione l'utilizzo di $_SESSION['redirect'] o set_cookie() per passare questo valore. Un utente malintenzionato non può impostare un cookie su un dominio remoto a meno che non abbia XSS o HTTP Response Splitting - a quel punto XSS è molto più prezioso di un semplice reindirizzamento aperto.

    
risposta data 25.08.2016 - 08:32
fonte

Leggi altre domande sui tag