Filtrare qualsiasi URI di richiesta HTTP?


10

Voglio filtrare qualsiasi URI di richiesta HTTP eseguito tramite l'API HTTP.

Casi d'uso:

  1. Il controllo degli aggiornamenti di WordPress va su http://api.wordpress.org/core/version-check/1.6/ , ma https://api.wordpress.org/core/version-check/1.6/ funziona anche e voglio per usarlo sempre.
  2. Il nuovo file WordPress è tratto da http://wordpress.org/wordpress-3.4.2.zip , ma funziona anche https://wordpress.org/wordpress-3.4.2.zip .
  3. A volte voglio eseguire il debug delle richieste e reindirizzare quelle temporanee a un dominio personalizzato sul mio server locale.
  4. Alcuni plugin inviano richieste ad altri server e desidero sostituire queste richieste quando il server esterno non funziona.

Le richieste di aggiornamento sono le più importanti per ora, perché esiste ancora il bug non risolto 16778 ( ulteriori informazioni ) e le richieste HTTPS riducono il rischio di un attacco man-in-the-middle.

Ho cercato a fondo , ho studiato il codice di base ... ma sono finito come Nacin due anni fa:

Pensavo che avresti potuto filtrare l'URL di una richiesta HTTP, ma ora non riesco a trovarne uno.

Cosa mi sono perso? Ho fatto? :)


Collegando questa risposta qui per chiunque cerchi il debug cURL in WP.
Kaiser

Risposte:


9

Meno di una risposta, ma solo un elenco di cose direttamente dalla mia esperienza con esso - forse hai trascurato qualcosa.

Debug della richiesta e dei suoi risultati

Senza approfondire troppo il processo di aggiornamento, ma l'API HTTP WP utilizza la WP_HTTPclasse. Offre anche una bella cosa: un hook di debug.

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

Dove $responsepuò anche essere un WP_Erroroggetto che forse ti dice di più.

Nota: da un breve test, questo filtro sembra funzionare (per qualche motivo) solo se lo posizioni il più vicino possibile a dove stai effettivamente facendo la richiesta. Quindi forse è necessario chiamarlo dall'interno di una richiamata su uno dei filtri di seguito.

WP_HTTP Argomenti di classe

Gli argomenti Classes sono filtrabili, ma alcuni vengono ripristinati dai metodi interni a ciò che WP ritiene necessario.

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

Uno degli argomenti è ssl_verify, che è vero per impostazione predefinita (ma per me causa enormi problemi durante l'aggiornamento da - ad esempio - GitHub). Modifica: dopo aver eseguito il debug di una richiesta di test, ho trovato un altro argomento impostato per verificare se SSL è impostato su true. Si chiama sslverify(senza separare il trattino basso). Non ho idea di dove sia entrato nel gioco, se è effettivamente in uso o abbandonato e se hai la possibilità di influenzarne il valore. L'ho trovato usando il 'http_api_debug'filtro.

Completamente personalizzato

Puoi anche "semplicemente" sovrascrivere tutti gli interni e procedere con una configurazione personalizzata. C'è un filtro per quello.

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

Il primo argomento deve essere impostato su true. Quindi puoi interagire con gli argomenti all'interno $re il risultato da parse_url( $url );.

delega

Un'altra cosa che potrebbe funzionare potrebbe essere l'esecuzione di tutto attraverso un proxy personalizzato. Ciò richiede alcune impostazioni nel tuo wp-config.php. Non l'ho mai provato prima, ma ho analizzato le costanti un po 'di tempo fa e ho riassunto alcuni esempi che dovrebbero funzionare e ho incluso alcuni commenti nel caso in cui ne avessi bisogno un giorno. Devi definire WP_PROXY_HOSTe WP_PROXY_PORTcome un min. ambientazione. Altrimenti nulla funzionerà e semplicemente ignorerà il tuo 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' );

MODIFICARE

La WP_HTTPClasse normalmente funge da classe base (verrà estesa per diversi scenari). Estendentisi WP_HTTP_*classi sono Fsockopen, Streams, Curl, Proxy, Cookie, Encoding. Se si aggancia un callback 'http_api_debug'all'azione, il terzo argomento ti dirà quale classe è stata utilizzata per la tua richiesta.

All'interno della WP_HTTP_curlclasse troverai il request()metodo. Questo metodo offre due filtri per intercettare il comportamento SSL: uno per le richieste locali 'https_local_ssl_verify'e uno per le richieste remote 'https_ssl_verify'. WP probabilmente definirà localcome localhoste da cosa ottenere in cambio get_option( 'siteurl' );.

Quindi quello che farei è provare quanto segue subito prima di fare quella richiesta (o da un callback collegato alla richiesta più vicina:

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: nella maggior parte dei casi WP_HTTP_curlverranno utilizzati per gestire i proxy.


1
Ah, penso che tu abbia risposto indirettamente alla mia domanda: posso collegarmi pre_http_request, annullare la richiesta e inviarla nuovamente con l'URL corretto. Ci proverò stasera.
fuxia

8

Sulla base della risposta utile di @ kaiser, ho scritto del codice che sembra funzionare bene. Questo è il motivo per cui l'ho contrassegnato come la risposta.

Lasciami spiegare la mia soluzione ...

La logica

Quando viene eseguita una richiesta inviata tramite l'API WP_Http::request(). Questo è il metodo con ...

@todo Refactor questo codice.

... nella sua intestazione. Non potrei essere più d'accordo.

Ora ci sono alcuni filtri. Ho deciso di abusare pre_http_requestdelle mie esigenze:

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

Otteniamo tre argomenti qui: false, $r, $url.

  • falseè il valore di ritorno atteso per apply_filters(). Se rispediamo qualcos'altro, WordPress si interrompe immediatamente e la richiesta originale non verrà inviata.

  • $rè una matrice di argomenti per quella richiesta. Dobbiamo cambiare anche questi in un minuto.

  • $urlè - sorpresa! - l'URL.

Quindi nel nostro callback t5_update_wp_per_https()guardiamo l'URL, e se è un URL che vogliamo filtrare, diciamo NO a WordPress non dicendo "no" ( false).

inserisci qui la descrizione dell'immagine

Nota a margine: Di seguito puoi prevenire tutte le richieste HTTP con:
add_filter( 'pre_http_request', '__return_true' );

Accettiamo invece la nostra richiesta con un URL migliore e argomenti leggermente modificati ( $r, rinominati in $argsper leggibilità).

Il codice

Si prega di leggere i commenti in linea, sono importanti.

<?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;
}

I test

Senza quel plugin utilizzato da WordPress:

  • http://api.wordpress.org/core/version-check/1.6/ per i controlli di aggiornamento e
  • http://wordpress.org/wordpress-3.4.2.zip per scaricare i nuovi file.

L'ho provato con due installazioni locali, un unico sito e una configurazione multi-sito su Win 7. Per forzare un set di aggiornamento I $wp_versionin wp-includes/version.phpa 1e la versione di TwentyEleven a 1.3.

Per guardare il traffico di rete ho usato Wireshark : è gratuito, funziona su Windows e Linux e offre alcuni strumenti di filtro impressionanti.

Guardare HTTPS è un po 'difficile: vedi solo dati crittografati ... questa è l'idea dopo tutto. Per vedere se il mio plugin ha fatto quello che dovrebbe fare, ho guardato prima il traffico non crittografato e ho notato l'indirizzo IP utilizzato per connettersi a wordpress.org. Quello era 72.233.56.138, a volte 72.233.56.139.
Non sorprende, esiste un bilanciamento del carico e probabilmente molti altri strumenti, quindi non possiamo fare affidamento su un indirizzo IP.

Quindi ho inserito ip.addr == 72.233.56.138la maschera di filtro, attivato il plug-in, sono andato a wp-admin/update-core.phpcercare il traffico a Wireshark. Le linee verdi sono richieste in testo semplice, esattamente ciò che non vogliamo. Le linee rosse e nere sono un segno di successo.

Wireshark

Il controllo degli aggiornamenti è andato bene: ha trovato le versioni “più recenti”. Anche gli aggiornamenti effettivi per il tema e il nucleo sono andati bene. Esattamente quello di cui avevo bisogno.

E ancora ... ciò potrebbe essere più semplice se ci fosse un semplice filtro per l'URL.


1
+1 per /* /**/, solo perché è geniale. E (se potessi) un altro +1 per Charles Bronson. E poi ci dovrebbe essere un altro +1 per la spiegazione dettagliata, i commenti e gli screenshot.
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);

1
Va notato che in questo esempio di codice, nella funzione http_api_curl_custom, la variabile globale $ replace_url dovrebbe essere impostata su NULL dopo che è stata utilizzata. Altrimenti, dopo la prima occorrenza di "wordpress.org" nell'URL, tutti gli altri URL verrebbero sostituiti.
MihanEntalpo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.