Filtrer tout URI de requête HTTP?

10

Je souhaite filtrer tout URI de requête HTTP effectué 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 pour l'utiliser toujours.
  2. Le nouveau fichier WordPress est extrait de http://wordpress.org/wordpress-3.4.2.zip , mais https://wordpress.org/wordpress-3.4.2.zip fonctionne également.
  3. Parfois, je veux déboguer les demandes et rediriger celles temporaires vers un domaine personnalisé sur mon serveur local.
  4. Certains plugins font des requêtes à d'autres serveurs, et je veux remplacer ces requêtes lorsque le serveur externe tombe en panne.

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

J'ai cherché en profondeur , j'ai étudié le code de base… mais j'ai fini comme Nacin il y a deux ans:

Je pensais à coup sûr que vous pouviez filtrer l'URL d'une demande HTTP, mais maintenant je n'en trouve plus.

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

fuxia
la source
Lier cette réponse ici pour toute personne qui recherche le débogage cURL dans WP.
kaiser

Réponses:

9

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

Débogage de la requête et de ses résultats

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

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

$responsepeut aussi être un WP_Errorobjet qui vous en dit peut-être plus.

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

WP_HTTP Arguments de classe

Les arguments Classes lui-même sont filtrables, mais certains afaik sont réinitialisés par les méthodes internes à ce que WP suppose être 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 qui est défini pour vérifier si SSL est défini sur true. Il est appelé sslverify(sans séparer le trait de soulignement). Aucune idée d'où cela est entré 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 'http_api_debug'filtre.

Complètement personnalisé

Vous pouvez également "simplement" remplacer tous les composants internes et opter pour 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 à l'intérieur $ret le résultat de parse_url( $url );.

Procuration

Une autre chose qui pourrait fonctionner pourrait être de tout exécuter via un proxy personnalisé. Cela nécessite quelques paramètres dans votre wp-config.php. Je n'ai jamais essayé cela auparavant, mais j'ai parcouru les constantes il y a quelque temps et j'ai résumé quelques exemples qui devraient fonctionner et inclus quelques commentaires au cas où j'en aurais besoin un jour. Vous devez définir WP_PROXY_HOSTet en WP_PROXY_PORTtant que min. réglage. Sinon, rien ne fonctionnera et il 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' );

ÉDITER

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

À l'intérieur de la WP_HTTP_curlclasse, vous trouverez la request()méthode. 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 définira probablement au localfur localhostet à mesure ce que vous obtenez en retour get_option( 'siteurl' );.

Donc, ce que je ferais, c'est d'essayer ce qui suit juste avant de faire cette demande (ou à partir d'un rappel 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' );

Sidenote: Dans la plupart des cas, WP_HTTP_curlsera utilisé pour gérer les procurations.

kaiser
la source
1
Ah, je pense que vous avez répondu à ma question indirectement: je peux me connecter pre_http_request, annuler la demande et la renvoyer avec l'URL correcte. Je vais essayer ça ce soir.
fuxia
8

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

Laissez-moi vous expliquer ma solution…

La logique

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

@todo Refactorisez 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_requestde mes besoins:

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

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

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

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

  • $urlest - surprise! - l'URL.

Donc, dans notre rappel, t5_update_wp_per_https()nous regardons l'URL, et si c'est une URL que nous voulons filtrer, nous disons NON à WordPress en ne disant pas «non» ( false).

entrez la description de l'image ici

Remarque: Il s'ensuit que vous pouvez empêcher toutes les requêtes HTTP avec:
add_filter( 'pre_http_request', '__return_true' );

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

Le code

Veuillez 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 site unique et une configuration multi-site sur Win 7. Pour forcer une mise à jour ensemble I $wp_versiondans wp-includes/version.phpla 1et la version de TwentyEleven à 1.3.

Pour surveiller le trafic réseau, j'ai utilisé Wireshark : c'est gratuit, il fonctionne sur Windows et Linux, et il propose des outils de filtrage impressionnants.

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

Ensuite, j'ai tapé ip.addr == 72.233.56.138dans le masque de filtre, activé le plugin, suis allé voir wp-admin/update-core.phple trafic dans Wireshark. Les lignes vertes sont des demandes en texte clair - exactement ce que nous ne voulons pas. Les lignes rouges et noires sont un signe de réussite.

Wireshark

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

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

fuxia
la source
1
+1 pour /* /**/, juste parce que c'est du génie. Et (si je pouvais) un autre +1 pour Charles Bronson. Et puis il devrait y avoir un autre +1 pour l'explication détaillée, les commentaires et les captures d'écran.
kaiser
3
    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);
Oleg Butuzov
la source
1
Il convient de mentionner que dans cet exemple de code, dans la fonction http_api_curl_custom, la variable globale $ replace_url doit être définie sur NULL après son utilisation. Sinon, après la première occurrence de "wordpress.org" dans l'url, toutes les autres URL seront remplacées.
MihanEntalpo