Überprüfen Sie die anfordernde URL

9

Verwenden von WP 4.8.2

Was ist der beste Weg, um die anfordernde URL zu überprüfen, wenn eine Anfrage mit der Rest-API verarbeitet wird?

Beispielsweise erhält eine Site eine Anfrage und Sie möchten überprüfen, ob sie von einer "erlaubten" URL stammt. Und scheitern, wenn die URL nicht erlaubt ist.

Das funktioniert nicht:

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 );
shanebp
quelle
Nach dem Auskommentieren der Bedingung wird die Antwort weiterhin gesendet. Also denke ich, ich benutze den falschen Haken.
Shanebp
Haben Sie überprüft, wie $requestund $urlwie Vars über var_dumpoder ähnlich aussehen , finde ich, dass die Überprüfung der Ein- und Ausgänge immer zu einer richtigen Antwort führt.
Farinspace
2
Verweisende URL ist leicht zu fälschen und kann nicht für irgendeine Art von Sicherheit verwendet werden.
Milo
Wir verwenden Token und SSL. Wir möchten auch die verweisende URL überprüfen, unabhängig davon, ob sie gefälscht werden kann oder nicht.
Shanebp
2
Es handelt sich um eine API, die für das Web geöffnet ist. In diesem Artikel geht es um Schiedsrichter, da Authentifizierung und SSL einfach nicht relevant sind. Wahrscheinlich deaktivieren Sie auch den CORS-Schutz ... Sofern er nicht nur angemeldeten Benutzern zur Verfügung steht, hat er keine Sicherheit.
Mark Kaplun

Antworten:

5

Dieser Filter ist definitiv nicht der, den Sie suchen. Dieser Filter wird ausgelöst, bevor das Ergebnis zurückgegeben wird. Dies WP_REST_Request::from_url()scheint eine Factory-Methode zu sein, die nur intern zum Behandeln von Einbettungen verwendet wird.

Eine bessere Option ist die Rückgabe einer WP_ErrorInstanz im rest_pre_dispatchFilter .

Einige Einschränkungen:

Wie von @milo erwähnt, ist der Referer nicht zuverlässig und sollte nicht für eine Sicherheitsüberprüfung verwendet werden.

Darüber hinaus kann die Einstellung nicht garantiert werden.

Im Folgenden finden Sie ein Beispiel dafür, wie Sie den rest_pre_dispatchFilter verwenden können, um die Anforderung zum Fehlschlagen zu bringen, wenn sie von einem fehlerhaften Referer stammt:

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 );
ssnepenthe
quelle
4

Alles, was Sie vom Client erhalten, gilt als Benutzereingabe und sollte nicht als vertrauenswürdig eingestuft werden. Da der Header leicht manipuliert und missbraucht werden kann, empfehle ich, diese Methode nicht zu verwenden, wenn Sie sich bei vertraulichen Daten darauf verlassen.

Wenn die Anforderungen von einer Seite stammen, können Sie einen anderen Ansatz wählen. Andernfalls kann jeder von nirgendwo aus eine Anfrage an die API senden und den Referrer ändern.

Angenommen, Sie haben eine Reihe von Seiten, die als "Zulässig" gefiltert werden . Sie können nur für diese Seiten einen Nounce erstellen und diese dann in Ihrer Anfrage validieren.

Wenn ein Nounce vorhanden und gültig ist, ist die Anforderung zulässig. Andernfalls blockieren Sie es.

Jack Johansson
quelle
4
+1 ... es ist eine API ... die Annahme, dass Sie Anrufe nur von Browsern erhalten, ist lächerlich.
Mark Kaplun
Ja, ich denke, Nounce ist ein besserer Ansatz, da es nicht existiert, wenn jemand eine Anfrage direkt an die API sendet.
Jack Johansson
4

@ssnepenthes Antwort lautet zu Recht, dass der von Ihnen verwendete Hook bei eingehenden Anfragen nicht der richtige ist.

Anforderungsinformationen stehen PHP sofort zur Verfügung, sodass Sie den frühesten verfügbaren Hook verwenden können, um sie zu überprüfen. Wenn Sie dies im Kontext der Anforderungs-API tun möchten, sollten Sie den frühesten Hook einer REST-API-Anforderung verwenden. 'rest_pre_dispatch'Der von @ssnepenthe vorgeschlagene Vorschlag ist in Ordnung. Möglicherweiserest_authentication_errors können Sie auch einen Fehler zurückgeben, falls etwas nicht stimmt.

Aber Jack Johansson ist direkt sagen , dass HTTP - Header (wie die Referer - Header verwendet in @ ssnepenthe des aswer) sind nicht vertrauenswürdig, da sie sehr leicht vom Client geändert. Es wäre also so, als würde man einen Wachmann vor eine Tür stellen, der nur fragt: "Es ist sicher, Sie eintreten zu lassen?" an alle, die reingehen wollen: das wird nicht funktionieren.

Aber die von Jack Johansson vorgeschlagene Antwort (ein Nonce) ist auch keine echte Lösung: Der springende Punkt bei Nonces ist, sich mit der Zeit zu ändern, und ein öffentlicher API-Endpunkt kann keine Dinge haben, die sich basierend auf der Zeit ändern. Darüber hinaus sind WP-Nonces nur dann vertrauenswürdig, wenn ein angemeldeter Benutzer vorhanden ist, was bei einer öffentlichen API möglicherweise nicht der Fall ist. Wenn ein Benutzer angemeldet ist, gibt es wahrscheinlich keinen Grund, die eingehende Domäne zu überprüfen: Sie vertrauen dem Benutzer, nicht dem Benutzermaschine.

Also, was tun?

Selbst wenn HTTP-Header nicht vertrauenswürdig sind, stammen nicht alle verfügbaren Informationen $_SERVERaus Headern.

Normalerweise stammen alle $_SERVERWerte, deren Schlüssel mit beginnen, HTTP_aus Kopfzeilen und müssen als unsichere Benutzereingaben behandelt werden .

Aber zum Beispiel $_SERVER['REMOTE_ADDR']enthält die IP - Adresse für die TCP - Verbindung zu Ihrem Server verwendet, was bedeutet , es ist vertrauenswürdiger 1 .

Was auch bedeutet, dass entweder:

  • Richtiges Konfigurieren des Servers zum Generieren des $_SERVER['REMOTE_HOST']Werts (zum Beispiel in Apache, den Sie HostnameLookups Onin Ihrem benötigen httpd.conf) dieses Werts
  • Verwenden Sie gethostbyaddrdiese Option, um eine umgekehrte DNS-Suche durchzuführen, um den Domänennamen der in gespeicherten IP-Adresse aufzulösen$_SERVER['REMOTE_ADDR']

Sie könnten recht zuverlässig einen Hostnamen erhalten , dass Sie gegen eine weiße Liste zu überprüfen , verwenden könnten (für den Code, können Sie den Code von @ ssnepenthe des aswer anzupassen , wo Sie ersetzen würden $referer = $request->get_header('referer')mit $referer = gethostbyaddr($_SERVER['REMOTE_ADDR'])).

Aber es gibt ein Problem .

Befindet sich Ihr Webserver hinter einem Reverse-Proxy (eigentlich eine recht häufige Lösung), wird die TCP-Verbindung zum Webserver tatsächlich vom Proxy hergestellt. Dies ist $_SERVER['REMOTE_ADDR']die IP des Proxys und nicht die IP des Clients, der die Anforderung ursprünglich gesendet hat.

Die ursprüngliche Anforderungs-IP ist in solchen Fällen normalerweise als verfügbar $_SERVER['HTTP_X_FORWARDED_FOR'], aber einer der $_SERVERWerte, die damit beginnen HTTP_, ist nicht wirklich vertrauenswürdig.

Wenn sich Ihr Webserver also hinter einem Reverse-Proxy 2 befindet , $_SERVER['REMOTE_ADDR']wäre selbst dieser für einen solchen Schutz nicht nützlich, und eine domänenbasierte Whitelist könnte nur auf Proxy-Ebene implementiert werden.

Kurz gesagt, eine zuverlässige Lösung für die API-Endpunktsicherung sollte entweder mithilfe eines echten Authentifizierungsmechanismus (z. B. oAuth) implementiert werden oder direkt auf der Serverkonfiguration und nicht auf Anwendungsebene erfolgen.


Anmerkungen

1 Nun, theoretisch könnte es kaputt gehen, wenn jemand Ihren ISP gehackt hat oder wenn ein Angreifer aus Ihrem LAN heraus agiert. In beiden Fällen können Sie nur sehr wenig tun, um sicher zu gehen.

2 Wenn Sie nicht wissen, ob Sie sich hinter einem Reverse-Proxy befinden, können Sie eine Anfrage von Ihrem lokalen PC senden und prüfen, ob $_SERVER['REMOTE_ADDR']auf dem Server die lokale PC-IP übereinstimmt und ob sie $_SERVER['HTTP_X_FORWARDED_FOR']vorhanden ist und mit der lokalen PC-IP übereinstimmt.

gmazzap
quelle
Das OP versucht, den Referrer zu erhalten, daher habe ich angenommen, dass er dies auf einer Seite tun möchte, ohne die API direkt zu pingen.
Jack Johansson
@JackJohansson eigentlich hat der OP den Referer nie erwähnt :) Sie sagen, sie wollen "überprüfen, ob er von einer 'erlaubten' URL stammt", was klingt, als würden sie den API-Endpunkt für bestimmte Domänen auf die Whitelist setzen, und auch das Code-Snippet in OP gibt dasselbe Idee zu mir.
gmazzap