web-dev-qa-db-fra.com

Filtrer n'importe quel URI de requête HTTP?

Je souhaite filtrer toute adresse URI de requête HTTP effectuée via l'API HTTP.

Cas d'utilisation:

  1. La vérification de la mise à jour de WordPress va à http://api.wordpress.org/core/version-check/1.6/ , mais https://api.wordpress.org/core/version-check/1.6/ fonctionne aussi, et je veux l'utiliser toujours.
  2. Le nouveau fichier WordPress provient de http://wordpress.org/wordpress-3.4.2.Zip , mais https://wordpress.org/wordpress-3.4.2.Zip fonctionne également.
  3. Parfois, je souhaite déboguer des demandes et rediriger celles temporaires vers un domaine personnalisé sur mon serveur local.
  4. Certains plug-ins font des demandes à d'autres serveurs et je souhaite les remplacer lorsque le serveur externe tombe en panne.

Les demandes de mise à jour sont les plus importantes pour l’instant, car il ya toujours le bogue non corrigé 16778 ( plus d’informations ), et les requêtes HTTPS réduisent le risque d’une attaque Man-in-the-middle.

J'ai cherchéminutieusement , j'ai étudié le code noyau… mais j'ai fini comme Nacin il y a deux ans:

Je pensais pouvoir filtrer l'URL d'une requête HTTP, mais je ne parviens pas à en trouver une.

Qu'est-ce que j'ai raté? Ai-je? :)

9
fuxia

Moins qu'une réponse, mais juste une liste de choses directement de mon expérience avec cela - peut-être avez-vous oublié quelque chose.

Débogage de la demande et de ses résultats

Sans entrer trop dans le processus de mise à jour, mais l'API HTTP WP utilise la classe WP_HTTP. Il offre également une bonne chose: un crochet de débogage.

do_action( 'http_api_debug', $response, 'response', $class, $args, $url );

$response peut également être un objet WP_Error qui en dit peut-être plus.

Remarque: à partir d'un bref test, ce filtre ne semble fonctionner (pour une raison quelconque) que si vous le placez sous la forme close à l'endroit où vous faites réellement la demande. Alors peut-être devez-vous l'appeler à partir d'un rappel sur l'un des filtres ci-dessous.

WP_HTTP Arguments de classe

Les arguments Classes sont eux-mêmes filtrables, mais certains sont réinitialisés par les méthodes internes à ce que WP suppose nécessaire.

apply_filters( 'http_request_args', $r, $url );

L'un des arguments est ssl_verify, ce qui est vrai par défaut (mais pour moi, cela pose d'énormes problèmes lors de la mise à jour depuis, par exemple, GitHub). Edit: Après avoir débogué une demande de test, j'ai trouvé un autre argument configuré pour vérifier si SSL est défini sur true. Cela s'appelle sslverify (sans séparer le tiret bas). Aucune idée d'où cela entre dans le jeu, s'il est réellement utilisé ou abandonné et si vous avez une chance d'influencer sa valeur. Je l'ai trouvé en utilisant le filtre 'http_api_debug'.

Complètement personnalisé

Vous pouvez également "simplement" remplacer tous les internes et utiliser une configuration personnalisée. Il y a un filtre pour ça.

apply_filters( 'pre_http_request', false, $r, $url );

Le premier argument doit être défini sur true. Ensuite, vous pouvez interagir avec les arguments contenus dans $r et le résultat de parse_url( $url );.

Procuration

Une autre chose qui pourrait fonctionner pourrait être tout exécuter par un proxy personnalisé. Cela nécessite quelques paramètres dans votre wp-config.php. Je n’avais jamais essayé cela auparavant, mais j’ai passé en revue les constantes il ya quelque temps et ai résumé quelques exemples de ce que devrait travailler et y ai ajouté des commentaires au cas où j’en aurais besoin un jour. Vous devez définir WP_PROXY_Host et WP_PROXY_PORT en tant que min. réglage. Sinon, rien ne fonctionnera et cela contournera simplement votre proxy.

# HTTP Proxies
# Used for e.g. in Intranets
# Fixes Feeds as well
# Defines the proxy adresse.
define( 'WP_PROXY_Host',          '127.0.84.1' );
# Defines the proxy port.
define( 'WP_PROXY_PORT',          '8080' );
# Defines the proxy username.
define( 'WP_PROXY_USERNAME',      'my_user_name' );
# Defines the proxy password.
define( 'WP_PROXY_PASSWORD',      'my_password' );
# Allows you to define some adresses which
# shouldn't be passed through a proxy.
define( 'WP_PROXY_BYPASS_HOSTS',  'localhost, www.example.com' );

MODIFIER

La classe WP_HTTP agit normalement comme base class (sera étendue pour différents scénarios). Les classes WP_HTTP_* extensibles sont Fsockopen, Streams, Curl, Proxy, Cookie, Encoding. Si vous associez un rappel à l'action 'http_api_debug'-, le troisième argument vous indiquera quelle classe a été utilisée pour votre demande.

Dans la classe WP_HTTP_curl, vous trouverez la méthode request(). Cette méthode propose deux filtres pour intercepter le comportement SSL: un pour les requêtes locales 'https_local_ssl_verify' et un pour les requêtes distantes 'https_ssl_verify'. WP va probablement définir local comme localhost et ce que vous obtenez en retour de get_option( 'siteurl' );.

Donc, ce que je ferais est d'essayer le droit suivant avant de faire cette demande (ou d'un rappel qui est lié à la demande la plus proche:

add_filter( 'https_ssl_verify', '__return_true' );

# Local requests should be checked with something like
# 'localhost' === $_SERVER['HTTP_Host'] or similar
# add_filter( 'https_local_ssl_verify', '__return_true' );

Note: Dans la plupart des cas, WP_HTTP_curl sera utilisé pour gérer les procurations.

8
kaiser

Sur la base de la réponse utile de @ kaiser, j’ai écrit un code qui semble bien fonctionner. C'est la raison pour laquelle je l'ai marquée comme étant la réponse.

Laissez-moi vous expliquer ma solution…

La logique

Lorsqu'une demande envoyée via l'API est exécutée via WP_Http::request(). C’est la méthode avec…

@todo Refactor ce code.

… Dans son en-tête. Je ne pourrais pas être plus d'accord.

Maintenant, il y a des filtres. J'ai décidé d'abuser pre_http_request pour mes besoins:

add_filter( 'pre_http_request', 't5_update_wp_per_https', 10, 3 );

Nous avons trois arguments ici: false, $r, $url.

  • false est la valeur de retour attendue pour apply_filters(). Si nous renvoyons autre chose, WordPress s’arrête immédiatement et la demande initiale ne sera pas envoyée.

  • $r est un tableau d'arguments pour cette demande. Nous devons aussi les changer en une minute.

  • $url est - surprise! - l'URL.

Donc dans notre callback t5_update_wp_per_https() nous regardons l'URL, et si c'est une URL que nous voulons filtrer, nous disonsNONà WordPress par non disant "non" (false) .

enter image description here

Note latérale: Vous pouvez empêcher toutes les requêtes HTTP avec:
add_filter( 'pre_http_request', '__return_true' );

Nous lançons notre propre requête à la place avec une meilleure URL et des arguments légèrement ajustés ($r, renommé en $args pour plus de lisibilité).

Le code

S'il vous plaît lire les commentaires en ligne, ils sont importants.

<?php
/**
 * Plugin Name: T5 Update WP per HTTPS
 * Description: Forces update checks and downloads for WP to use HTTPS.
 * Plugin URI:  http://wordpress.stackexchange.com/questions/72529/filter-any-http-request-uri
 * Version:     2012.11.14
 * Author:      Thomas Scholz
 * Author URI:  http://toscho.de
 * Licence:     MIT
 * License URI: http://opensource.org/licenses/MIT
 */

add_filter( 'pre_http_request', 't5_update_wp_per_https', 10, 3 );

/**
 * Force HTTPS requests for update checks and new WP version downloads.
 *
 * @wp-hook pre_http_request
 * @param   bool   $false
 * @param   array  $args
 * @param   string $url
 * @return  FALSE|array|object FALSE if everything is okay, an array of request
 *                            results or an WP_Error instance.
 */
function t5_update_wp_per_https( $false, $args, $url )
{
    // Split the URL into useful parts.
    $url_data = parse_url( $url );

    // It is already HTTPS.
    if ( 'https' === strtolower( $url_data['scheme'] ) )
        return FALSE;

    // Not our Host.
    if ( FALSE === stripos( $url_data['Host'], 'wordpress.org' ) )
        return FALSE;

    // Make that an HTTPS request.
    $new_url = substr_replace( $url, 'https', 0, 4 );

    // WP_Http cannot verify the wordpress.org certificate.
    $args['sslverify'] = FALSE;

    // It is slow. We wait at least 30 seconds.
    30 > $args['timeout'] and $args['timeout'] = 30;

    // Get an instance of WP_Http.
    $http    = _wp_http_get_object();

    // Get the result.
    $result = $http->request( $new_url, $args );

    /* prepend this line with a '#' to debug like a boss.
    print '<pre>'
    . htmlspecialchars( print_r( $result, TRUE ), ENT_QUOTES, 'utf-8', FALSE )
    . '</pre>';
    die();
    /**/

    return $result;
}

Les tests

Sans ce plugin WordPress utilisé:

  • http://api.wordpress.org/core/version-check/1.6/ pour les vérifications de mise à jour, et
  • http://wordpress.org/wordpress-3.4.2.Zip pour télécharger les nouveaux fichiers.

Je l'ai testé avec deux installations locales, un seul site et une configuration multi-sites sous Win 7. Pour forcer une mise à jour, je règle $wp_version dans wp-includes/version.php sur 1 et la version de TwentyEleven sur 1.3.

Pour regarder le trafic réseau que j’avais utilisé Wireshark : C’est gratuit, il tourne sous Windows et Linux, et il offre des outils de filtrage impressionnants.

Regarder HTTPS est un peu difficile: vous ne voyez que des données chiffrées, c’est l’idée après tout. Pour voir si mon plug-in a fait ce qu'il devrait faire, j'ai d'abord regardé le trafic non chiffré et noté l'adresse IP utilisée pour se connecter à wordpress.org. C'était 72.233.56.138, parfois 72.233.56.139.
Sans surprise, il existe un équilibreur de charge et probablement de nombreux autres outils. Nous ne pouvons donc pas compter sur one adresse IP.

Ensuite, j'ai tapé ip.addr == 72.233.56.138 dans le masque de filtrage, activé le plug-in, suis allé à wp-admin/update-core.php et surveillé le trafic dans Wireshark. Les lignes vertes sont des demandes en texte brut - exactement ce que nous ne voulons pas. Les lignes rouges et noires sont un signe de succès.

Wireshark

La vérification de la mise à jour s'est bien déroulée: les versions "plus récentes" ont été trouvées. Les mises à jour réelles pour le thème et le noyau se sont bien déroulées aussi. Exactement ce dont j'avais besoin.

Et encore… cela pourrait être plus facile s'il y avait un simple filtre pour l'URL.

8
fuxia
    add_filter('http_request_args', 'http_request_args_custom', 10,2);
    function http_request_args_custom($request,$url){
            if (strpos($url, 'wordpress.org') !== false){
                    global $replaced_url;
                    $replaced_url = 'http://wordpress.local';
            }
            return $request;
    }

    add_action('http_api_curl', 'http_api_curl_custom');
    function http_api_curl_custom(&$handle){
            global $replaced_url;
            if (!is_null($replaced_url))
                    curl_setopt( $handle, CURLOPT_URL, $replaced_url);
    }

    $http = new WP_Http();
    $response = $http->request('http://wordpress.org', array());

    var_dump($response);
1
Butuzov