web-dev-qa-db-fra.com

vérifier l'URL du demandeur

Utilisation de WP 4.8.2

Quel est le meilleur moyen de vérifier l'URL du demandeur lors du traitement d'une demande avec rest-api?

Par exemple, un site reçoit une demande et vous souhaitez vérifier si elle provient d'une URL "autorisée". Et échouez si l'URL n'est pas autorisée.

Cela ne fonctionne pas:

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 );
7
shanebp

Ce filtre n’est certainement pas celui que vous recherchez. Ce filtre se déclenche avant de renvoyer le résultat de WP_REST_Request::from_url(), qui semble être une méthode usine uniquement utilisée en interne pour gérer les incorporations.

Une meilleure option consiste à renvoyer une instance WP_Error sur le filtre rest_pre_dispatch .

Quelques mises en garde:

Comme mentionné par @milo, le référant n'est pas fiable et ne doit pas être utilisé pour un contrôle de sécurité.

De plus, il n'est pas garanti d'être défini.

Voici un exemple d'utilisation du filtre rest_pre_dispatch pour que la demande échoue si elle provient d'un mauvais référent:

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 );
5
ssnepenthe

Tout ce que vous recevez du client est considéré comme une entrée utilisateur et ne doit pas être approuvé. Comme l'en-tête peut être facilement manipulé et utilisé de manière abusive, ma suggestion est de ne pas utiliser cette méthode si vous vous en fiez à des données sensibles.

Si les demandes proviennent d'une page, vous pouvez utiliser une autre approche. Sinon, n'importe qui peut envoyer une demande à l'API de nulle part et modifier le référent.

Disons que vous avez un tas de pages qui sont filtrées comme "Autorisé" . Vous pouvez créer une once uniquement pour ces pages, puis les valider dans votre demande.

Si une once existe et est valide, la demande est autorisée. Sinon, bloquez-le.

4
Jack Johansson

@ssnepenthe 'aswer a raison de dire que le hook que vous utilisez n'est pas le bon dans la requête entrante.

Les informations de demande sont immédiatement disponibles pour PHP, vous pouvez donc utiliser le plus ancien crochet disponible pour les vérifier. Et si vous souhaitez effectuer cette opération dans le contexte de l'API de demande, vous devez utiliser le plus ancien point d'ancrage d'une demande d'API REST. 'rest_pre_dispatch' suggéré par @ssnepenthe _ convient, une autre option pourrait être rest_authentication_errors qui vous permettrait de renvoyer une erreur au cas où quelque chose ne va pas.

Mais Jack Johansson a raison dans dit que les en-têtes HTTP (comme celui du référent utilisé dans @ssnepenthe) ne sont pas fiables, car ils sont très facilement modifiés par le client. Donc, ce serait comme mettre un agent de sécurité devant une porte qui demande simplement "il est prudent de vous laisser entrer?" à quiconque veut y aller: ça ne va pas au travail.

Mais la solution proposée par Jack Johansson (un nonce) n’est pas non plus une vraie solution: l’intérêt des nonces est de changer avec le temps, et un point de terminaison d’API public ne peut pas avoir de changement en fonction du temps. De plus, WP nonces ne sont fiables que s’il existe un utilisateur connecté, ce qui peut ne pas être le cas pour une API publique et si un utilisateur est connecté, il n’ya probablement aucune raison de vérifier le domaine entrant: vous faites confiance à l'utilisateur, pas à la machine de l'utilisateur.

Alors que faire?

Bien, même si les en-têtes HTTP ne sont pas fiables, toutes les informations disponibles sur $_SERVER ne proviennent pas d’en-têtes.

Normalement, toutes les valeurs $_SERVER dont la clé commence par HTTP_ proviennent d'en-têtes et doivent être traitées comme entrée utilisateur non sécurisée _.

Cependant, par exemple, $_SERVER['REMOTE_ADDR'] contient l'adresse IP utilisée pour la connexion TCP à votre serveur, ce qui signifie que est fiable.1.

Ce qui signifie aussi que soit:

  • configurer correctement le serveur pour générer la valeur $_SERVER['REMOTE_Host'] (par exemple, dans Apache, vous aurez besoin de HostnameLookups On dans votre httpd.conf) cette valeur
  • utiliser gethostbyaddr pour effectuer une recherche DNS inversée afin de résoudre le nom de domaine de l'IP stockée dans $_SERVER['REMOTE_ADDR']

vous pouvez obtenir de manière assez fiable un nom d'hôte que vous pouvez utiliser pour vérifier une liste blanche (pour le code, vous pouvez l'adapter à partir de la requête de @ ssnepenthe, où vous remplaceriez $referer = $request->get_header('referer') par $referer = gethostbyaddr($_SERVER['REMOTE_ADDR'])).

Mais il y a un issue.

Si votre serveur Web est derrière un proxy inverse (solution assez courante, en fait), la connexion TCP au serveur Web est en fait établie par le proxy. Par conséquent, $_SERVER['REMOTE_ADDR'] sera l'adresse IP du proxy, et non celle du serveur. client qui a initialement envoyé la demande.

Dans de tels cas, l'adresse IP de la demande d'origine est généralement disponible sous la forme $_SERVER['HTTP_X_FORWARDED_FOR'], mais étant l'une de ces valeurs $_SERVER commençant par HTTP_, elle n'est pas vraiment fiable.

Donc, si votre serveur Web est derrière un proxy inverse2 même le $_SERVER['REMOTE_ADDR'] ne serait pas utile pour une telle protection et une liste blanche basée sur un domaine ne pourrait être mise en œuvre qu'au niveau du proxy.

En bref, une solution fiable pour la sécurisation des points de terminaison d'API doit être implémentée à l'aide d'un mécanisme d'authentification réel (par exemple, oAuth) ou bien directement sur la configuration du serveur et non au niveau de l'application.


Remarques

1 Eh bien, en théorie, cela pourrait être cassé si quelqu'un piratait votre fournisseur de services Internet ou si un attaquant agissait depuis l'intérieur de votre réseau local, dans les deux cas, vous ne pouvez que faire très peu pour être en sécurité.

2 Si vous ne savez pas si vous êtes derrière un proxy inverse, vous pouvez envoyer une demande à partir de votre PC local et vérifier si $_SERVER['REMOTE_ADDR'] sur le serveur correspond à l'adresse IP du PC local et si $_SERVER['HTTP_X_FORWARDED_FOR'] est présent et correspond à l'adresse IP du PC local.

4
gmazzap