controlla l'URL richiedente


9

Utilizzo di WP 4.8.2

Qual è il modo migliore per controllare l'URL richiedente quando elabora una richiesta con il resto-API?

Ad esempio, un sito riceve una richiesta e si desidera verificare se proviene da un URL "consentito". E fallire se l'URL non è permesso.

Questo non funziona:

function my_check_request_url( $request, $url ) {

    $bits = parse_url( $url );

    if ( $bits['host'] != 'example.com' )
       $request = false;

    return $request;

}
add_filter( 'rest_request_from_url', 'my_check_request_url', 10, 2 );

Dopo aver commentato il condizionale, la risposta viene comunque inviata. Quindi penso di usare l'hook sbagliato.
shanebp,

hai verificato che aspetto ha $requeste che $urlvaria da var_dumpo simile, trovo che l'ispezione degli ingressi e delle uscite porti sempre a una risposta corretta.
farinspace,

2
l'URL di riferimento è facilmente simulato e non può essere utilizzato per alcun tipo di sicurezza.
Milo,

Stiamo usando token e SSL. Vorremmo anche controllare l'URL di riferimento, indipendentemente dal fatto che possa essere simulato o meno.
Shanebp,

2
si tratta di un'API aperta al Web, di cosa si parla in questo arbitri in quanto autenticazione e SSL non sono rilevanti. Probabilmente stai anche disabilitando le protezioni CORS ... A meno che non sia disponibile solo per gli utenti che hanno effettuato l'accesso, questo non ha alcuna sicurezza.
Mark Kaplun,

Risposte:


5

Quel filtro non è sicuramente quello che stai cercando. Tale filtro viene attivato prima di restituire il risultato WP_REST_Request::from_url()che sembra essere un metodo di fabbrica che viene utilizzato solo internamente per gestire gli incorporamenti.

Un'opzione migliore è quella di restituire WP_Errorun'istanza sul rest_pre_dispatchfiltro .

Alcuni avvertimenti:

Come menzionato da @milo, il referer non è affidabile e non dovrebbe essere usato per un controllo di sicurezza.

Inoltre, non è garantito che sia impostato.

Con quelli fuori mano, ecco un esempio di come potresti usare il rest_pre_dispatchfiltro per far fallire la richiesta se proviene da un referer errato:

function wpse281916_rest_check_referer( $result, $server, $request ) {
    if ( null !== $result ) {
        // Core starts with a null value.
        // If it is no longer null, another callback has claimed this request.
        // Up to you how to handle - for this example we will just return early.
        return $result;
    }

    $referer = $request->get_header( 'referer' );

    if ( ! $referer ) {
        // Referer header is not set - If referer is required, return a WP_Error instance instead.
        return $result;
    }

    $host = wp_parse_url( $referer, PHP_URL_HOST );

    if ( ! $host ) {
        // Referer is malformed - If referer is required, return a WP_Error instance instead.
        return $result;
    }

    if ( 'mysite.com' !== $host ) {
        // Referer is set to something that we don't allow.
        return new WP_Error(
            'invalid-referer',
            'Requests must contain a valid referer',
            compact( 'referer' )
        );
    }

    // Otherwise we are good - return original result and let WordPress handle as usual.
    return $result;
}
add_filter( 'rest_pre_dispatch', 'wpse281916_rest_check_referer', 10, 3 );

4

Tutto ciò che ricevi dal client è considerato input dell'utente e non dovrebbe essere attendibile. Poiché l'intestazione può essere facilmente manipolata e abusata, il mio suggerimento è di non utilizzare questo metodo se si fa affidamento su di esso per i dati sensibili.

Se le richieste provengono da una pagina, puoi avere un altro approccio. Altrimenti, chiunque può inviare una richiesta all'API dal nulla e modificare il referrer.

Supponiamo che tu abbia un sacco di pagine che vengono filtrate come "Consentite" . Puoi creare un annuncio solo per queste pagine e quindi convalidarle nella tua richiesta.

Se esiste un avviso ed è valido, la richiesta è consentita. Altrimenti, bloccalo.


4
+1 ... è un'API .... il presupposto che ricevi chiamate solo dai browser è ridicolo.
Mark Kaplun,

Sì, penso che il nounce sia un approccio migliore poiché non esiste se qualcuno invia direttamente una richiesta all'API.
Jack Johansson

4

La risposta di @ssnepenthe ha ragione nel dire che l'hook che stai usando non è quello giusto nella richiesta in arrivo.

Le informazioni sulla richiesta sono immediatamente disponibili per PHP, quindi è possibile utilizzare il primo hook disponibile per verificarle. E se vuoi farlo nel contesto dell'API di richiesta, dovresti usare il primo hook di una richiesta API REST. 'rest_pre_dispatch'suggerito da @ssnepenthe va bene, forse un'altra opzione potrebbe essere rest_authentication_errorsche ti consentirebbe di restituire un errore nel caso in cui qualcosa non va.

Ma Jack Johansson ha ragione nel dire che le intestazioni HTTP (come l'intestazione di riferimento usata nella risposta di @ssnepenthe) non sono affidabili, in quanto possono essere facilmente cambiate dal client. Quindi sarebbe come mettere una guardia di sicurezza davanti a una porta che chiede semplicemente "è sicuro di farti entrare?" a chiunque voglia entrare: non funzionerà.

Ma la soluzione proponeva che la risposta di Jack Johansson (un nonce) non fosse nemmeno una vera soluzione: l'intero punto delle nonces è cambiare con il tempo e un endpoint pubblico dell'API non può avere cose che cambiano in base al tempo. Inoltre, le nonces WP sono affidabili solo quando c'è un utente connesso, il che potrebbe non essere il caso di un'API pubblica e se un utente ha effettuato l'accesso, probabilmente non c'è motivo di controllare il dominio in arrivo: ti fidi dell'utente, non del macchina dell'utente.

Quindi che si fa?

Bene, anche se le intestazioni HTTP non sono affidabili, non tutte le informazioni disponibili $_SERVERprovengono dalle intestazioni.

Normalmente, tutti i $_SERVERvalori le cui chiavi iniziano con cui inizia HTTP_provengono dalle intestazioni e devono essere trattati come input dell'utente non sicuro .

Ma, ad esempio, $_SERVER['REMOTE_ADDR']contiene l'indirizzo IP utilizzato per la connessione TCP al server, il che significa che è affidabile 1 .

Il che significa anche che:

  • configurando correttamente il server per generare il $_SERVER['REMOTE_HOST']valore (ad esempio in Apache ti servirà HostnameLookups Onall'interno del tuo httpd.conf) quel valore
  • usando gethostbyaddrper fare una ricerca DNS inversa per risolvere il nome di dominio dell'IP memorizzato in$_SERVER['REMOTE_ADDR']

si potrebbe ottenere molto affidabile un nome host che si potrebbe usare per controllare contro una whitelist (per il codice, è possibile adattare il codice da @ di ssnepenthe risponderti in cui si desidera sostituirlo $referer = $request->get_header('referer')con $referer = gethostbyaddr($_SERVER['REMOTE_ADDR'])).

Ma c'è un problema .

Se il tuo server web si trova dietro un proxy inverso (soluzione abbastanza comune, in realtà) la connessione TCP al server web viene effettivamente effettuata dal proxy, quindi $_SERVER['REMOTE_ADDR']sarà l'IP del proxy e non l'IP del client che ha originariamente inviato la richiesta.

L'IP di richiesta originale in questi casi è solitamente disponibile come $_SERVER['HTTP_X_FORWARDED_FOR'], ma essendo uno di quei $_SERVERvalori che iniziano con HTTP_esso non è veramente affidabile.

Quindi, se il tuo server web è dietro un proxy inverso 2, anche $_SERVER['REMOTE_ADDR']questo non sarebbe utile per tale protezione e una whitelist basata su dominio potrebbe essere implementata solo a livello di proxy.

In breve, una soluzione affidabile per la protezione degli endpoint API dovrebbe essere implementata utilizzando un meccanismo di autenticazione reale (ad esempio oAuth) o dovrebbe essere eseguita agendo direttamente sulla configurazione del server e non a livello di applicazione.


Appunti

1 Beh, in teoria potrebbe essere rotto se qualcuno ha violato il tuo ISP o se un attaccante agisce dall'interno della tua LAN, in entrambi i casi c'è molto poco che potresti fare per essere al sicuro.

2 Se non si sa se si è dietro un proxy inverso, è possibile inviare una richiesta dal PC locale e verificare se $_SERVER['REMOTE_ADDR']sul server corrisponde l'IP del PC locale e anche se $_SERVER['HTTP_X_FORWARDED_FOR']è presente e corrisponde all'IP del PC locale.


L'OP sta cercando di ottenere il referrer, quindi ho pensato che volesse farlo su una pagina, senza eseguire il ping diretto dell'API.
Jack Johansson

@JackJohansson in realtà l'OP non ha mai menzionato referer :) Dicono che vogliono "controllare se proviene da un URL" consentito " che sembra stiano cercando nella whitelist endpoint API per domini specifici, e anche lo snippet di codice in OP dà lo stesso idea per me.
gmazzap
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.