HTTP-Anforderungs-URI filtern?

10

Ich möchte jeden HTTP-Anforderungs-URI filtern, der über die HTTP-API ausgeführt wird.

Anwendungsfälle:

  1. Die Überprüfung des WordPress-Updates erfolgt unter http://api.wordpress.org/core/version-check/1.6/ , aber https://api.wordpress.org/core/version-check/1.6/ funktioniert auch, und ich möchte dies immer zu benutzen.
  2. Die neue WordPress-Datei stammt von http://wordpress.org/wordpress-3.4.2.zip , aber https://wordpress.org/wordpress-3.4.2.zip funktioniert auch.
  3. Manchmal möchte ich Anforderungen debuggen und diese temporär an eine benutzerdefinierte Domäne auf meinem lokalen Server umleiten.
  4. Einige Plugins stellen Anforderungen an andere Server, und ich möchte diese Anforderungen ersetzen, wenn der externe Server ausfällt.

Die Aktualisierungsanforderungen sind vorerst die wichtigsten, da der Fehler 16778 ( weitere Informationen ) immer noch nicht behoben ist und HTTPS-Anforderungen das Risiko eines Man-in-the-Middle-Angriffs verringern .

Ich habe gründlich gesucht , den Kerncode studiert… aber vor zwei Jahren wie Nacin gelandet:

Ich war mir sicher, dass Sie die URL einer HTTP-Anfrage filtern können, aber jetzt kann ich keine finden.

Was habe ich verpasst? Habe ich? :) :)

Fuxia
quelle
Verknüpfen Sie diese Antwort hier für alle, die in WP nach cURL-Debugging suchen.
Kaiser

Antworten:

9

Weniger als eine Antwort, aber nur eine Liste von Dingen, die direkt aus meiner Erfahrung damit stammen - vielleicht haben Sie etwas übersehen.

Debuggen der Anfrage und ihrer Ergebnisse

Ohne zu tief in den Aktualisierungsprozess einzudringen, aber die WP HTTP API verwendet die WP_HTTPKlasse. Es bietet auch eine schöne Sache: Ein Debug-Hook.

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

Wo $responsekann auch ein WP_ErrorObjekt sein, das Ihnen vielleicht mehr sagt.

Hinweis: Nach einem kurzen Test scheint dieser Filter (aus irgendeinem Grund) nur zu funktionieren, wenn Sie ihn so nahe an der Stelle platzieren, an der Sie die Anforderung tatsächlich ausführen. Vielleicht müssen Sie es innerhalb eines Rückrufs auf einem der folgenden Filter aufrufen.

WP_HTTP Klassenargumente

Die Classes-Argumente selbst sind filterbar, aber einige werden von den internen Methoden der Methoden auf das zurückgesetzt, was WP für erforderlich hält.

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

Eines der Argumente ist ssl_verify, dass dies standardmäßig zutrifft (aber für mich verursacht es massive Probleme beim Aktualisieren von beispielsweise GitHub). Bearbeiten: Nach dem Debuggen einer Testanforderung habe ich ein anderes Argument gefunden, mit dem überprüft werden soll, ob SSL aktiviert ist true. Es heißt sslverify(ohne Unterstrich). Keine Ahnung, wo dies ins Spiel kam, ob es tatsächlich benutzt oder aufgegeben wird und ob Sie die Chance haben, seinen Wert zu beeinflussen. Ich habe es mit dem 'http_api_debug'Filter gefunden.

Völlig individuell

Sie können auch "einfach" die gesamten Interna überschreiben und ein benutzerdefiniertes Setup durchführen. Dafür gibt es einen Filter.

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

Das erste Argument muss auf true gesetzt werden. Dann können Sie mit den Argumenten im Inneren $rund dem Ergebnis von interagieren parse_url( $url );.

Proxy

Eine andere Sache, die funktionieren könnte, könnte sein, alles über einen benutzerdefinierten Proxy auszuführen. Dies erfordert einige Einstellungen in Ihrem wp-config.php. Ich habe das noch nie versucht, aber ich habe vor einiger Zeit die Konstanten durchgearbeitet und einige Beispiele zusammengefasst, die funktionieren sollten , und einige Kommentare hinzugefügt, falls ich sie eines Tages brauche. Sie müssen definieren WP_PROXY_HOSTund WP_PROXY_PORTals min. Rahmen. Sonst funktioniert nichts und es wird einfach Ihr Proxy umgangen.

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

BEARBEITEN

Die WP_HTTPKlasse fungiert normalerweise als Basisklasse (wird für verschiedene Szenarien erweitert). Die Verlängerungs WP_HTTP_*Klassen sind Fsockopen, Streams, Curl, Proxy, Cookie, Encoding. Wenn Sie einen Rückruf an die 'http_api_debug'-action verknüpfen, gibt das dritte Argument an, welche Klasse für Ihre Anforderung verwendet wurde.

In der WP_HTTP_curlKlasse finden Sie die request()Methode. Diese Methode bietet zwei Filter zum Abfangen des SSL-Verhaltens: einen für lokale Anforderungen 'https_local_ssl_verify'und einen für Remote-Anforderungen 'https_ssl_verify'. WP wird wahrscheinlich definieren, localals localhostund was Sie als Gegenleistung erhalten get_option( 'siteurl' );.

Ich würde also Folgendes versuchen, bevor Sie diese Anforderung ausführen (oder von einem Rückruf, der mit der nächstgelegenen Anforderung verknüpft ist:

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

Nebenbemerkung: In den meisten Fällen WP_HTTP_curlwerden Proxies verwendet.

Kaiser
quelle
1
Ah, ich denke, Sie haben meine Frage indirekt beantwortet: Ich kann mich pre_http_requesteinbinden, die Anfrage abbrechen und sie mit der richtigen URL erneut senden. Werde das heute Abend versuchen.
Fuxia
8

Basierend auf der nützlichen Antwort von @ kaiser habe ich einen Code geschrieben, der gut zu funktionieren scheint. Das ist der Grund, warum ich es als Die Antwort markiert habe.

Lassen Sie mich meine Lösung erklären ...

Die Logik

Wenn eine über die API gesendete Anforderung ausgeführt wird WP_Http::request(). Das ist die Methode mit…

@todo Refactor diesen Code.

… In seiner Kopfzeile. Ich konnte nicht mehr zustimmen.

Nun gibt es einige Filter. Ich habe beschlossen, pre_http_requestfür meine Bedürfnisse zu missbrauchen :

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

Wir bekommen hier drei Argumente : false, $r, $url.

  • falseist der erwartete Rückgabewert für apply_filters(). Wenn wir etwas anderes zurückschicken, stoppt WordPress sofort und die ursprüngliche Anfrage wird nicht gesendet.

  • $rist ein Array von Argumenten für diese Anforderung. Auch diese müssen wir in einer Minute ändern.

  • $urlist - Überraschung! - die URL.

In unserem Rückruf sehen t5_update_wp_per_https()wir uns also die URL an. Wenn es sich um eine URL handelt, die wir filtern möchten, sagen wir NEIN zu WordPress, indem wir nicht "Nein" sagen ( false).

Geben Sie hier die Bildbeschreibung ein

Randnotiz: Folglich können Sie alle HTTP-Anforderungen verhindern mit:
add_filter( 'pre_http_request', '__return_true' );

Wir feuern unseren eigenen Wunsch statt mit einer besseren URL und angepasst Argumenten leicht ( $rumbenannt , um $argsLesbarkeit zu verbessern).

Der Code

Bitte lesen Sie die Inline-Kommentare, sie sind wichtig.

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

Die Tests

Ohne dieses Plugin verwendet WordPress:

  • http://api.wordpress.org/core/version-check/1.6/ für Update-Prüfungen und
  • http://wordpress.org/wordpress-3.4.2.zip um die neuen Dateien herunterzuladen.

Getestet habe ich es mit zwei lokalen Installationen, einer einzigen Seite einer Multi-Site - Setup auf Win 7. Um ein Update I Satz zu erzwingen $wp_versionin wp-includes/version.phpzu 1und die Version von TwentyEleven zu 1.3.

Um den Netzwerkverkehr zu beobachten, habe ich Wireshark verwendet : Es ist kostenlos, läuft unter Windows und Linux und bietet einige beeindruckende Filter-Tools.

Das Anschauen von HTTPS ist etwas schwierig: Sie sehen nur verschlüsselte Daten… das ist schließlich die Idee. Um zu sehen, ob mein Plugin das getan hat, was es tun sollte, habe ich zuerst den unverschlüsselten Verkehr beobachtet und die IP-Adresse notiert, die für die Verbindung zu wordpress.org verwendet wurde. Das war 72.233.56.138manchmal so 72.233.56.139.
Kein Wunder, dass es einen Load Balancer und wahrscheinlich viele andere Tools gibt, sodass wir uns nicht auf eine IP-Adresse verlassen können.

Dann tippte ich ip.addr == 72.233.56.138in die Filtermaske, aktivierte das Plugin, ging zu wp-admin/update-core.phpund beobachtete den Verkehr in Wireshark. Grüne Linien sind Anfragen im Klartext - genau das, was wir nicht wollen. Die roten und schwarzen Linien sind ein Zeichen des Erfolgs.

Wireshark

Die Update-Prüfung verlief einwandfrei: Es wurden die "neueren" Versionen gefunden. Die eigentlichen Updates für das Thema und den Kern gingen auch gut. Genau das, was ich brauchte.

Und trotzdem… das könnte einfacher sein, wenn es einen einfachen Filter für die URL gäbe.

Fuxia
quelle
1
+1 für /* /**/, nur weil es genial ist. Und (wenn ich könnte) noch +1 für Charles Bronson. Und dann sollte es noch eine +1 für die detaillierten Erklärungen, Kommentare und Screenshots geben.
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
quelle
1
Es sollte erwähnt werden, dass in diesem Codebeispiel in der Funktion http_api_curl_custom die globale Variable $ replace_url nach ihrer Verwendung auf NULL gesetzt werden sollte. Andernfalls würden nach dem ersten Auftreten von "wordpress.org" in der URL alle anderen URLs ersetzt.
MihanEntalpo