Filtrer tout URI de requête HTTP?

9

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 est effectuée à l'adresse enlace , mais enlace fonctionne également, et je souhaite l’utiliser toujours.
  2. Le nouveau fichier WordPress provient de enlace , mais enlace 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-in adressent des requêtes à d'autres serveurs et je souhaite les remplacer en cas de panne du serveur externe.

Les demandes de mise à jour sont les plus importantes pour le moment, car il y a toujours le fichier non corrigé bug 16778 ( Plus d’informations ) et les requêtes HTTPS réduisent le risque d’attaque par interception.

J'ai recherché minutieusement , j'ai étudié le code principal… mais je me suis retrouvé 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 manqué? Ai-je? :)

    
posée fuxia 14.11.2012 - 08:08

3 réponses

8

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 & 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 vous en dit peut-être plus.

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

WP_HTTP arguments de classe

Les arguments Classes eux-mêmes peuvent être filtrés, mais certains d'entre eux sont réinitialisés par les méthodes internes à ce que WP considère nécessaire.

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

L'un des arguments est ssl_verify , ce qui est vrai par défaut (mais provoque pour moi des problèmes énormes lors de la mise à jour depuis, par exemple, GitHub). Éditer: Après avoir débogué une demande de test, j'ai trouvé un autre argument défini pour vérifier si SSL est défini sur true . Cela s'appelle sslverify (sans séparateur de soulignement). 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" écraser tous les éléments internes et utiliser une configuration personnalisée. Il existe un filtre pour cela.

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

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

Proxy

Une autre chose qui pourrait fonctionner pourrait être de tout exécuter via un proxy personnalisé. Cela nécessite quelques réglages dans votre wp-config.php . Je n’avais jamais essayé cela auparavant, mais j’ai passé en revue les constantes il y a quelque temps et ai résumé quelques exemples de ce que devrait fonctionner. J'ai également 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 votre proxy sera simplement contourné.

# 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' );

EDIT

La classe WP_HTTP agit normalement comme une classe de base (sera étendue pour différents scénarios). Les classes WP_HTTP_* d'extension sont Fsockopen , Streams , Curl , Proxy , Cookie , Encoding . Si vous associez un rappel à l'action 'http_api_debug' -action, 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 demandes locales 'https_local_ssl_verify' et un pour les demandes distantes 'https_ssl_verify' . WP définira probablement local comme localhost et ce que vous obtiendrez en retour de get_option( 'siteurl' ); .

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

Remarque: dans la plupart des cas, WP_HTTP_curl sera utilisé pour traiter les mandataires.

    
réponse donnée kaiser 14.11.2012 - 11:42
8

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 suis tout à fait d’accord.

Maintenant, il y a quelques filtres. J'ai décidé d'abuser de 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 quelque chose d'autre, WordPress s'arrête immédiatement et la demande initiale ne sera pas envoyée.

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

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

Ainsi, dans notre rappel t5_update_wp_per_https() , nous examinons l'URL. S'il s'agit d'une URL à filtrer, nous disons NON à WordPress par pas . non ”( false ).

Remarquesecondaire:vouspouvezempêchertouteslesrequêtesHTTPavec:
add_filter('pre_http_request','__return_true');

NousenvoyonsnotreproprerequêteàlaplaceavecunemeilleureURLetdesargumentslégèrementajustés($r,renommé$argspourplusdelisibilité).

Lecode

Veuillezlirelescommentairesenligne,ilssontimportants.

<?php/***PluginName:T5UpdateWPperHTTPS*Description:ForcesupdatechecksanddownloadsforWPtouseHTTPS.*PluginURI:http://wordpress.stackexchange.com/questions/72529/filter-any-http-request-uri*Version:2012.11.14*Author:ThomasScholz*AuthorURI:http://toscho.de*Licence:MIT*LicenseURI:http://opensource.org/licenses/MIT*/add_filter('pre_http_request','t5_update_wp_per_https',10,3);/***ForceHTTPSrequestsforupdatechecksandnewWPversiondownloads.**@wp-hookpre_http_request*@parambool$false*@paramarray$args*@paramstring$url*@returnFALSE|array|objectFALSEifeverythingisokay,anarrayofrequest*resultsoranWP_Errorinstance.*/functiont5_update_wp_per_https($false,$args,$url){//SplittheURLintousefulparts.$url_data=parse_url($url);//ItisalreadyHTTPS.if('https'===strtolower($url_data['scheme']))returnFALSE;//Notourhost.if(FALSE===stripos($url_data['host'],'wordpress.org'))returnFALSE;//MakethatanHTTPSrequest.$new_url=substr_replace($url,'https',0,4);//WP_Httpcannotverifythewordpress.orgcertificate.$args['sslverify']=FALSE;//Itisslow.Wewaitatleast30seconds.30>$args['timeout']and$args['timeout']=30;//GetaninstanceofWP_Http.$http=_wp_http_get_object();//Gettheresult.$result=$http->request($new_url,$args);/*prependthislinewitha'#'todebuglikeaboss.print'<pre>'.htmlspecialchars(print_r($result,TRUE),ENT_QUOTES,'utf-8',FALSE).'</pre>';die();/**/return$result;}

Lestests

SanscepluginWordPressutilisé:

  • http://api.wordpress.org/core/version-check/1.6/pourlesvérificationsdemiseàjouret
  • http://wordpress.org/wordpress-3.4.2.zippourtéléchargerlesnouveauxfichiers.

Jel'aitestéavecdeuxinstallationslocales,unseulsiteetuneconfigurationmulti-sitessousWin7.Pourforcerunemiseàjour,définissez$wp_versiondanswp-includes/version.phpsur1etlaversiondeTwentyElevento1.3.

Poursurveillerletraficsurleréseauquej'aiutilisé, Wireshark : il est gratuit, il fonctionne sous Windows et Linux et propose un filtre impressionnant. outils.

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 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 une une adresse IP.

Ensuite, j'ai tapé ip.addr == 72.233.56.138 dans le masque de filtrage, activé le plug-in, accédé à 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.

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.

    
réponse donnée fuxia 15.11.2012 - 02:03
1
    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);
    
réponse donnée Butuzov 14.11.2012 - 08:23

Lire d'autres questions sur les étiquettes