¿Filtrar cualquier URI de solicitud HTTP?

10

Quiero filtrar cualquier URI de solicitud HTTP realizada a través de la API HTTP.

Casos de uso:

  1. La verificación de actualización de WordPress va a http://api.wordpress.org/core/version-check/1.6/ , pero https://api.wordpress.org/core/version-check/1.6/ también funciona, y quiero para usar esto siempre.
  2. El nuevo archivo de WordPress está tomado de http://wordpress.org/wordpress-3.4.2.zip , pero https://wordpress.org/wordpress-3.4.2.zip también funciona.
  3. A veces quiero depurar solicitudes y redirigirlas temporalmente a un dominio personalizado en mi servidor local.
  4. Algunos complementos realizan solicitudes a otros servidores, y quiero reemplazar estas solicitudes cuando el servidor externo deja de funcionar.

Las solicitudes de actualización son las más importantes por ahora, porque todavía existe el error no corregido 16778 ( más información ), y las solicitudes HTTPS reducen el riesgo de un ataque Man-in-the-middle.

He buscado a fondo , he estudiado el código central ... pero terminé como Nacin hace dos años:

Pensé con seguridad que podría filtrar la URL de una solicitud HTTP, pero ahora no puedo encontrar una.

¿Qué me perdí? ¿Hice? :)

fuxia
fuente
Vincular esta respuesta aquí para cualquier persona que busque la depuración de cURL en WP.
Kaiser

Respuestas:

9

Menos de una respuesta, pero solo una lista de cosas directamente de mi experiencia con él, tal vez has pasado por alto algo.

Depuración de la solicitud y sus resultados

Sin profundizar demasiado en el proceso de actualización, pero la API HTTP de WP usa la WP_HTTPclase. También ofrece algo agradable: un gancho de depuración.

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

Donde $responsetambién puede haber un WP_Errorobjeto que quizás te diga más.

Nota: a partir de una breve prueba, este filtro parece funcionar solo (por alguna razón) si lo coloca tan cerca de donde realmente está haciendo la solicitud. Entonces, tal vez deba llamarlo desde una devolución de llamada en uno de los siguientes filtros.

WP_HTTP Argumentos de clase

Los argumentos de las clases en sí son filtrables, pero algunos pueden restablecerse mediante los métodos internos de nuevo a lo que WP supone que es necesario.

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

Uno de los argumentos es ssl_verify, que es cierto por defecto (pero para mí causa problemas masivos al actualizar desde, por ejemplo, GitHub). Editar: después de depurar una solicitud de prueba, encontré otro argumento configurado para verificar si SSL está configurado como true. Se llama sslverify(sin separar el guión bajo). No tengo idea de dónde entró esto en el juego, si realmente está en uso o abandonado y si tienes la oportunidad de influir en su valor. Lo encontré usando el 'http_api_debug'filtro.

Completamente personalizado

También puede "simplemente" anular todas las partes internas e ir con una configuración personalizada. Hay un filtro para eso.

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

El primer argumento debe establecerse en verdadero. Entonces puedes interactuar con los argumentos dentro $ry el resultado de parse_url( $url );.

Apoderado

Otra cosa que podría funcionar podría ser ejecutar todo a través de un Proxy personalizado. Esto necesita algunas configuraciones en su wp-config.php. Nunca he intentado esto antes, pero revisé las constantes hace un tiempo y resumí algunos ejemplos que deberían funcionar e incluí algunos comentarios en caso de que algún día lo necesite. Tienes que definir WP_PROXY_HOSTy WP_PROXY_PORTcomo min. ajuste. De lo contrario, nada funcionará y simplemente pasará por alto su 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' );

EDITAR

La WP_HTTPclase normalmente actúa como clase base (se extenderá para diferentes escenarios). Los extienden WP_HTTP_*clases son Fsockopen, Streams, Curl, Proxy, Cookie, Encoding. Si 'http_api_debug'conecta una devolución de llamada a la acción, el tercer argumento le dirá qué clase se utilizó para su solicitud.

Dentro de la WP_HTTP_curlclase, encontrarás el request()método. Este método ofrece dos filtros para interceptar el comportamiento SSL: uno para solicitudes locales 'https_local_ssl_verify'y otro para solicitudes remotas 'https_ssl_verify'. WP probablemente definirá localcomo localhosty de qué obtienes a cambio get_option( 'siteurl' );.

Entonces, lo que debería hacer es intentar lo siguiente justo antes de hacer esa solicitud (o de una devolución de llamada que esté conectada a la solicitud más cercana:

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

Nota al margen: en la mayoría de los casos WP_HTTP_curlse utilizará para manejar Proxies.

emperador
fuente
1
Ah, creo que has respondido mi pregunta indirectamente: puedo conectarme pre_http_request, cancelar la solicitud y volver a enviarla con la URL correcta. Lo intentaremos esta noche.
fuxia
8

Basado en la útil respuesta de @ kaiser, he escrito un código que parece funcionar bien. Esa es la razón por la que lo marqué como La respuesta.

Déjame explicarte mi solución ...

La lógica

Cuando se ejecuta una solicitud enviada a través de la API WP_Http::request(). Ese es el método con ...

@todo Refactoriza este código.

... en su encabezado. No podría estar mas de acuerdo.

Ahora, hay algunos filtros. Decidí hacer un mal uso pre_http_requestpara mis necesidades:

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

Obtenemos tres argumentos aquí: false, $r, $url.

  • falsees el valor de retorno esperado para apply_filters(). Si devolvemos algo más, WordPress se detiene de inmediato y no se enviará la solicitud original.

  • $res una matriz de argumentos para esa solicitud. Tenemos que cambiar esto también en un minuto.

  • $urles - sorpresa! - la URL

Entonces, en nuestra devolución de llamada t5_update_wp_per_https()miramos la URL, y si es una URL que queremos filtrar, decimos NO a WordPress al no decir "no" ( false).

ingrese la descripción de la imagen aquí

Nota al margen: a continuación puede evitar todas las solicitudes HTTP con:
add_filter( 'pre_http_request', '__return_true' );

En su lugar, activamos nuestra propia solicitud con una mejor URL y argumentos ligeramente ajustados ( $rrebautizados $argspara facilitar la lectura).

El código

Lea los comentarios en línea, son importantes.

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

Los exámenes

Sin ese complemento, WordPress utilizó:

  • http://api.wordpress.org/core/version-check/1.6/ para verificaciones de actualizaciones, y
  • http://wordpress.org/wordpress-3.4.2.zip para descargar los nuevos archivos.

Lo he comprobado con dos instalaciones locales, un sitio único y una configuración multi-sitio en Windows 7. Para forzar una actualización de conjunto que $wp_versionen wp-includes/version.phpa 1, y la versión de TwentyEleven a 1.3.

Para ver el tráfico de la red, utilicé Wireshark : es gratis, se ejecuta en Windows y Linux, y ofrece algunas herramientas de filtro impresionantes.

Ver HTTPS es un poco difícil: ves datos cifrados ... esa es la idea después de todo. Para ver si mi complemento hizo lo que debería hacer, observé primero el tráfico no cifrado y noté la dirección IP utilizada para conectarme a wordpress.org. Eso fue 72.233.56.138, a veces 72.233.56.139.
No es sorprendente, hay un equilibrador de carga y probablemente muchas otras herramientas, por lo que no podemos confiar en una sola dirección IP.

Luego escribí ip.addr == 72.233.56.138en la máscara de filtro, activé el complemento, fui wp-admin/update-core.phpy observé el tráfico en Wireshark. Las líneas verdes son solicitudes en texto plano, exactamente lo que no queremos. Las líneas rojas y negras son un signo de éxito.

Wireshark

El chequeo de actualización salió bien: encontró las versiones "más nuevas". Las actualizaciones reales para el tema y el núcleo también funcionaron bien. Exactamente lo que necesitaba.

Y aún así ... eso podría ser más fácil si hubiera un filtro simple para la URL.

fuxia
fuente
1
+1 para /* /**/, solo porque es genial. Y (si pudiera) otro +1 para Charles Bronson. Y luego debería haber otro +1 para la explicación detallada, comentarios y capturas de pantalla.
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
fuente
1
Cabe mencionar que, en este ejemplo de código, en la función http_api_curl_custom, la variable global $ Reemplazado_url debe establecerse en NULL después de que se usó. De lo contrario, después de la primera aparición de "wordpress.org" en la url, todas las demás URL serían reemplazadas.
MihanEntalpo