vérifier l'url de demande

9

Utilisation de WP 4.8.2

Quelle est la meilleure façon de vérifier l'URL demandeuse lors du traitement d'une demande avec l'api rest?

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 );
shanebp
la source
Après avoir commenté le conditionnel, la réponse est toujours envoyée. Je pense donc que j'utilise le mauvais crochet.
shanebp
Avez - vous vérifié ce que $requestet $urlvars ressembler via var_dumpou similaire, je trouve que l' inspection des entrées et sorties conduisent toujours à une réponse appropriée.
farinspace
2
l'URL de référence est facilement truquée et ne peut être utilisée pour aucune sorte de sécurité.
Milo
Nous utilisons des jetons et SSL. Nous aimerions également vérifier l'URL de référence, qu'elle puisse ou non être falsifiée.
shanebp
2
c'est une API ouverte sur le web, qu'est-ce que cette discussion sur les arbitres comme l'authentification et SSL n'est tout simplement pas pertinente. Vous désactivez probablement aussi les protections CORS ... À moins qu'il ne soit disponible que pour les utilisateurs connectés, cela n'a aucune sécurité.
Mark Kaplun

Réponses:

5

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

Une meilleure option consiste à renvoyer une WP_Errorinstance sur le rest_pre_dispatchfiltre .

Quelques mises en garde:

Comme mentionné par @milo, le référent 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 réglé.

Avec ceux-ci à l'écart, voici un exemple de la façon dont vous pouvez utiliser le rest_pre_dispatchfiltre pour provoquer l'échec de la demande 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 );
ssnepenthe
la source
4

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 abusé, ma suggestion est de ne pas utiliser cette méthode si vous vous en remettez à des données sensibles.

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

Supposons que vous ayez un tas de pages filtrées comme "Autorisées" . Vous pouvez créer un nom uniquement pour ces pages, puis les valider dans votre demande.

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

Jack Johansson
la source
4
+1 ... c'est une API .... l'hypothèse que vous recevez des appels uniquement des navigateurs est ridicule.
Mark Kaplun
Oui, je pense que le noun est une meilleure approche car il n'existe pas si quelqu'un envoie directement une demande à l'API.
Jack Johansson
4

La réponse de @ssnepenthe a raison de dire que le crochet que vous utilisez n'est pas le bon dans la demande entrante.

Les informations de demande sont immédiatement disponibles pour PHP, vous pouvez donc utiliser le premier crochet disponible pour les vérifier. Et si vous souhaitez le faire dans le contexte de l'API de demande, vous devez utiliser le premier crochet d'une demande d'API REST. 'rest_pre_dispatch'suggéré par @ssnepenthe est très bien, peut-être une autre option pourrait rest_authentication_errorsvous permettre de retourner une erreur en cas de problème.

Mais Jack Johansson a raison de dire que les en-têtes HTTP (comme l'en-tête de référence utilisé dans l'aswer de @ ssnepenthe) ne sont pas fiables, car ils sont très facilement modifiés par le client. Donc, ce serait comme mettre un gardien de sécurité devant une porte qui demande simplement "c'est sûr de vous laisser entrer?" à tous ceux qui veulent y entrer: ça ne marchera pas.

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

Alors que faire?

Eh bien, même si les en-têtes HTTP ne sont pas fiables, toutes les informations disponibles ne $_SERVERproviennent pas des en-têtes.

Normalement, toutes les $_SERVERvaleurs dont les clés commencent par qui commencent HTTP_proviennent d'en-têtes et doivent être traitées comme des entrées utilisateur non sécurisées .

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

Ce qui signifie également que:

  • configurer correctement le serveur pour générer la $_SERVER['REMOTE_HOST']valeur (par exemple dans Apache dont vous aurez besoin HostnameLookups Ondans votre httpd.conf) cette valeur
  • utilisation gethostbyaddrpour effectuer une recherche DNS inversée pour résoudre le nom de domaine de l'adresse IP stockée dans$_SERVER['REMOTE_ADDR']

vous pourriez obtenir assez fiable un nom d'hôte que vous pouvez utiliser pour vérifier contre un whitelist (pour le code, vous pouvez adapter le code de @ la aswer de ssnepenthe où vous devez remplacer $referer = $request->get_header('referer')par $referer = gethostbyaddr($_SERVER['REMOTE_ADDR'])).

Mais il y a un problème .

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

Dans de tels cas, l'IP de requête d'origine est généralement disponible en tant que $_SERVER['HTTP_X_FORWARDED_FOR'], mais être l'une de ces $_SERVERvaleurs qui commencent par HTTP_n'est pas vraiment fiable.

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

En bref, une solution fiable pour la sécurisation des points de terminaison de l'API doit être implémentée à l'aide d'un mécanisme d'authentification réel (par exemple oAuth) ou doit être effectuée en agissant 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 FAI ou si un attaquant agissait de l'intérieur de votre réseau local, dans les deux cas, vous ne pouviez pas faire grand-chose 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']le serveur correspond à l'IP du PC local et également s'il $_SERVER['HTTP_X_FORWARDED_FOR']est présent et correspond à l'IP du PC local.

gmazzap
la source
L'OP essaie d'obtenir le référent, j'ai donc supposé qu'il voulait le faire sur une page, sans cingler directement l'API.
Jack Johansson
@JackJohansson en fait, l'OP n'a jamais mentionné le référent :) Ils disent qu'ils veulent "vérifier si cela provient d'une URL" autorisée "", ce qui semble qu'ils cherchent à mettre sur liste blanche le point de terminaison de l'API pour les domaines spécifiques, et l'extrait de code dans OP donne la même chose idée pour moi.
gmazzap