Filtrować dowolny identyfikator URI żądania HTTP?

10

Chcę odfiltrować dowolny identyfikator URI żądania HTTP wykonany za pośrednictwem interfejsu API HTTP.

Przypadków użycia:

  1. Sprawdzanie aktualizacji WordPress przechodzi do http://api.wordpress.org/core/version-check/1.6/ , ale https://api.wordpress.org/core/version-check/1.6/ też działa, i chcę używać tego zawsze.
  2. Nowy plik WordPress pochodzi z http://wordpress.org/wordpress-3.4.2.zip , ale https://wordpress.org/wordpress-3.4.2.zip również działa.
  3. Czasami chcę debugować żądania i przekierowywać je tymczasowo do niestandardowej domeny na moim serwerze lokalnym.
  4. Niektóre wtyczki wysyłają żądania do innych serwerów i chcę je zastąpić, gdy serwer zewnętrzny ulegnie awarii.

Żądania aktualizacji są na razie najważniejsze, ponieważ wciąż istnieje nierozwiązany błąd 16778 ( więcej informacji ), a żądania HTTPS zmniejszają ryzyko ataku Man-in-the-middle.

Mam szukał dokładnie , ja badali kod rdzenia ... ale skończyło się jak Nacin dwa lata temu:

Pomyślałem, że możesz odfiltrować adres URL żądania HTTP, ale teraz nie mogę go znaleźć.

Co mnie ominęło? Czy ja? :)

fuxia
źródło
Link do tej odpowiedzi tutaj dla każdego, kto szuka debugowania cURL w WP.
kaiser

Odpowiedzi:

9

Mniej niż odpowiedź, ale tylko lista rzeczy prosto z mojego doświadczenia - może coś przeoczyłeś.

Debugowanie żądania i jego wyników

Bez diggin „zbyt głęboko w proces aktualizacji, ale WP HTTP API używa WP_HTTPklasy. Oferuje również fajną rzecz: hak debugowania.

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

Gdzie $responsemoże być także WP_Errorprzedmiot, który może powiedzieć ci więcej.

Uwaga: po krótkim teście ten filtr wydaje się (z jakiegoś powodu) działać tylko wtedy, gdy umieścisz go tak blisko miejsca, w którym faktycznie wykonujesz żądanie. Może więc trzeba zadzwonić z oddzwaniania na jednym z poniższych filtrów.

WP_HTTP Argumenty klasowe

Same argumenty Klasy są filtrowalne, ale niektóre z nich są resetowane przez wewnętrzne metody z powrotem do tego, co WP zakłada, że ​​jest potrzebne.

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

Jednym z argumentów jest ssl_verify, co jest prawdą domyślnie (ale dla mnie powoduje ogromne problemy podczas aktualizacji z - na przykład - GitHub). Edycja: Po debugowaniu żądania testowego znalazłem inny argument, który jest ustawiony, aby sprawdzić, czy SSL jest ustawiony na true. Nazywa się sslverify(bez oddzielania podkreślenia). Nie mam pojęcia, gdzie to się pojawiło, czy jest faktycznie używane, czy porzucone i czy masz szansę wpłynąć na jego wartość. Znalazłem to za pomocą 'http_api_debug'filtra.

Całkowicie niestandardowy

Możesz również „po prostu” zastąpić wszystkie elementy wewnętrzne i przejść do niestandardowej konfiguracji. Jest do tego filtr.

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

Pierwszy argument musi być ustawiony na true. Następnie możesz wchodzić w interakcje z argumentami wewnątrz $ri wynikami parse_url( $url );.

Pełnomocnik

Kolejną rzeczą, która może działać, może być uruchamianie wszystkiego za pośrednictwem niestandardowego serwera proxy. To wymaga pewnych ustawień w twoim wp-config.php. Nigdy wcześniej tego nie próbowałem, ale jakiś czas temu przeglądałem stałe i podsumowałem kilka przykładów, które powinny zadziałać, i zamieściłem komentarze na wypadek, gdyby tego potrzebowałem. Musisz zdefiniować WP_PROXY_HOSTi WP_PROXY_PORTjako min. oprawa. W przeciwnym razie nic nie będzie działać i po prostu obejdzie serwer 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' );

EDYTOWAĆ

WP_HTTPKlasy zazwyczaj działa jako podstawowej klasy (będzie przedłużony o różnych scenariuszach). Przebiegające WP_HTTP_*zajęcia są Fsockopen, Streams, Curl, Proxy, Cookie, Encoding. Jeśli podłączysz wywołanie zwrotne do 'http_api_debug'-action, trzeci argument powie ci, która klasa została użyta dla twojego żądania.

Wewnątrz WP_HTTP_curlklasy znajdziesz request()metodę. Ta metoda oferuje dwa filtry do przechwytywania zachowania SSL: jeden dla żądań lokalnych 'https_local_ssl_verify'i jeden dla żądań zdalnych 'https_ssl_verify'. WP prawdopodobnie zdefiniuje localjako localhosti to, co otrzymasz w zamian get_option( 'siteurl' );.

Chciałbym więc spróbować wykonać następujące czynności bezpośrednio przed wykonaniem tego żądania (lub z wywołania zwrotnego, które jest powiązane z najbliższym żądaniem:

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: W większości przypadków WP_HTTP_curlbędą używane do obsługi proxy.

kajzer
źródło
1
Ach, myślę, że pośrednio odpowiedziałeś na moje pytanie: mogę się podłączyć pre_http_request, anulować żądanie i wysłać je ponownie z poprawnym adresem URL. Spróbuję tego dziś wieczorem.
fuxia
8

Na podstawie użytecznej odpowiedzi @ kaiser napisałem kod, który wydaje się działać dobrze. To dlatego oznaczyłem to jako odpowiedź.

Pozwól mi wyjaśnić moje rozwiązanie…

Logika

Kiedy żądanie wysłane przez API jest uruchamiane WP_Http::request(). To metoda z…

@todo Refaktoryzuj ten kod.

… W nagłówku. Nie mogłem się więcej zgodzić.

Teraz jest kilka filtrów. Postanowiłem nadużyć pre_http_requestdo moich potrzeb:

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

Dostajemy trzy argumenty tutaj: false, $r, $url.

  • falsejest oczekiwaną wartością zwracaną dla apply_filters(). Jeśli odeślemy cokolwiek innego, WordPress zatrzymuje się natychmiast, a pierwotne żądanie nie zostanie wysłane.

  • $rjest tablicą argumentów dla tego żądania. Te też musimy zmienić za minutę.

  • $urljest - niespodzianka! - adres URL.

Więc w naszym wywołaniu zwrotnym t5_update_wp_per_https()patrzymy na adres URL, a jeśli jest to adres URL, który chcemy filtrować, mówimy NIE WordPressowi, nie mówiąc „nie” ( false).

wprowadź opis zdjęcia tutaj

Uwaga dodatkowa: Wynika z tego, że możesz zapobiec wszystkim żądaniom HTTP za pomocą:
add_filter( 'pre_http_request', '__return_true' );

Zamiast tego uruchamiamy własne żądanie z lepszym adresem URL i nieznacznie dostosowanymi argumentami ( $rzmieniono ich nazwy $argsna czytelne).

Kod

Przeczytaj wbudowane komentarze, są one ważne.

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

Testy

Bez tej wtyczki WordPress zastosował:

  • http://api.wordpress.org/core/version-check/1.6/ do kontroli aktualizacji oraz
  • http://wordpress.org/wordpress-3.4.2.zip aby pobrać nowe pliki.

Przetestowałem to z dwiema lokalnymi instalacjami, jedną witryną i instalacją obejmującą wiele witryn w systemie Win 7. Aby wymusić aktualizację, $wp_versionw wp-includes/version.phpktórej się wprowadziłem, 1i wersję TwentyEleven na 1.3.

Do oglądania ruchu sieciowego użyłem Wireshark : jest bezpłatny, działa w systemach Windows i Linux oraz oferuje imponujące narzędzia do filtrowania.

Oglądanie HTTPS jest trochę trudne: widzisz tylko zaszyfrowane dane… w końcu taki jest pomysł. Aby sprawdzić, czy moja wtyczka działa tak, jak powinna, najpierw obserwowałem niezaszyfrowany ruch i zanotowałem adres IP używany do łączenia się z wordpress.org. 72.233.56.138Czasami tak było 72.233.56.139.
Nic dziwnego, że istnieje moduł równoważenia obciążenia i prawdopodobnie wiele innych narzędzi, więc nie możemy polegać na jednym adresie IP.

Następnie wpisałem ip.addr == 72.233.56.138maskę filtra, aktywowałem wtyczkę, podszedłem do wp-admin/update-core.phpi obserwowałem ruch w Wireshark. Zielone linie to żądania w postaci zwykłego tekstu - dokładnie tego, czego nie chcemy. Czerwone i czarne linie są znakiem sukcesu.

Wireshark

Sprawdzenie aktualizacji przebiegło pomyślnie: znaleziono „nowsze” wersje. Rzeczywiste aktualizacje motywu i rdzenia również poszły dobrze. Dokładnie to, czego potrzebowałem.

A jednak… to mogłoby być łatwiejsze, gdyby istniał prosty filtr adresu URL.

fuxia
źródło
1
+1 za /* /**/, tylko dlatego, że jest genialny. I (gdybym mógł) kolejne +1 dla Charlesa Bronsona. A potem powinno być kolejne +1 za szczegółowe wyjaśnienia, komentarze i zrzuty ekranu.
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 Butuzow
źródło
1
Należy wspomnieć, że w tym przykładzie kodu w funkcji http_api_curl_custom zmienną globalną $ replace_url należy ustawić na NULL po jej użyciu. W przeciwnym razie po pierwszym wystąpieniu „wordpress.org” w adresie URL wszystkie inne adresy URL zostaną zastąpione.
MihanEntalpo