Filtrar qualquer URI de solicitação HTTP?

10

Quero filtrar qualquer URI de solicitação HTTP feito por meio da API HTTP.

Casos de uso:

  1. A verificação de atualização do WordPress vai para http://api.wordpress.org/core/version-check/1.6/ , mas https://api.wordpress.org/core/version-check/1.6/ também funciona, e eu quero para usar isso sempre.
  2. O novo arquivo WordPress é obtido em http://wordpress.org/wordpress-3.4.2.zip , mas https://wordpress.org/wordpress-3.4.2.zip também funciona.
  3. Às vezes, quero depurar solicitações e redirecionar as temporárias para um domínio personalizado no meu servidor local.
  4. Alguns plug-ins fazem solicitações para outros servidores e quero substituí-las quando o servidor externo ficar inoperante.

As solicitações de atualização são as mais importantes por enquanto, porque ainda existe o bug não corrigido 16778 ( mais informações ), e as solicitações de HTTPS diminuem o risco de um ataque do tipo intermediário.

Eu pesquisei minuciosamente , estudei o código principal ... mas acabei como Nacin há dois anos:

Eu pensei que você poderia filtrar a URL de uma solicitação HTTP, mas agora não consigo encontrar uma.

Do que eu senti falta? Eu fiz? :)

fuxia
fonte
Vinculando esta resposta aqui para quem pesquisa a depuração de cURL no WP.
kaiser

Respostas:

9

Menos que uma resposta, mas apenas uma lista de coisas diretamente da minha experiência com ela - talvez você tenha esquecido alguma coisa.

Depurando a solicitação e seus resultados

Sem aprofundar muito no processo de atualização, mas a API HTTP do WP usa a WP_HTTPclasse Ele também oferece uma coisa interessante: um gancho de depuração.

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

Onde $responsetambém pode ser um WP_Errorobjeto que talvez lhe conte mais.

Nota: A partir de um breve teste, esse filtro parece funcionar (por algum motivo) apenas se você o colocar o mais próximo de onde você está realmente fazendo a solicitação. Talvez você precise chamá-lo em um retorno de chamada em um dos filtros abaixo.

WP_HTTP Argumentos de classe

Os argumentos de Classes em si são filtráveis, mas alguns são redefinidos pelos métodos internos de volta ao que o WP assume que é necessário.

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

Um dos argumentos é o ssl_verifyque é verdadeiro por padrão (mas para mim causa grandes problemas ao atualizar - por exemplo - GitHub). Edit: Depois de depurar uma solicitação de teste, encontrei outro argumento definido para verificar se o SSL está definido como true. É chamado sslverify(sem separar o sublinhado). Não faço ideia de onde isso entrou no jogo, se ele está realmente em uso ou abandonado e se você tem uma chance de influenciar seu valor. Encontrei-o usando o 'http_api_debug'filtro.

Completamente personalizado

Você também pode "simplesmente" substituir todos os componentes internos e fazer uma configuração personalizada. Existe um filtro para isso.

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

O primeiro argumento precisa ser definido como verdadeiro. Do que você pode interagir com os argumentos internos $re com o resultado parse_url( $url );.

Proxy

Outra coisa que pode funcionar pode ser executar tudo por meio de um Proxy personalizado. Isso precisa de algumas configurações no seu wp-config.php. Eu nunca tentei isso antes, mas repassei as constantes há algum tempo e resumi alguns exemplos que deveriam funcionar e incluí alguns comentários, caso eu precise um dia. Você tem que definir WP_PROXY_HOSTe WP_PROXY_PORTcomo um min. configuração. Caso contrário, nada funcionará e simplesmente ignorará seu 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

A WP_HTTPclasse normalmente atua como classe base (será estendida para diferentes cenários). Os que se estendem WP_HTTP_*classes são Fsockopen, Streams, Curl, Proxy, Cookie, Encoding. Se você conectar um retorno de chamada à 'http_api_debug'ação-, o terceiro argumento informará qual classe foi usada para sua solicitação.

Dentro da WP_HTTP_curlclasse, você encontrará o request()método. Este método oferece dois filtros para interceptar o comportamento do SSL: um para solicitações locais 'https_local_ssl_verify'e outro para solicitações remotas 'https_ssl_verify'. O WP provavelmente definirá localcomo localhoste o que você recebe em troca get_option( 'siteurl' );.

Então, o que eu faria é tentar o seguinte logo antes de fazer essa solicitação (ou de um retorno de chamada vinculado à solicitação mais próxima:

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: Na maioria dos casos WP_HTTP_curl, será usado para manipular proxies.

kaiser
fonte
11
Ah, acho que você respondeu minha pergunta indiretamente: posso conectar-me pre_http_request, cancelar a solicitação e reenviá-la com o URL correto. Vai tentar isso hoje à noite.
fuxia
8

Com base na resposta útil do @ kaiser, escrevi um código que parece funcionar bem. Essa é a razão pela qual eu o marquei como A resposta.

Deixe-me explicar minha solução ...

A lógica

Quando uma solicitação enviada pela API é executada WP_Http::request(). Esse é o método com ...

@todo Refatore este código.

... no cabeçalho. Eu não poderia concordar mais.

Agora, existem alguns filtros. Decidi usar indevidamente pre_http_requestpara minhas necessidades:

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

Ficamos com três argumentos aqui: false, $r, $url.

  • falseé o valor de retorno esperado para apply_filters(). Se enviarmos mais alguma coisa de volta, o WordPress será interrompido imediatamente e a solicitação original não será enviada.

  • $ré uma matriz de argumentos para essa solicitação. Temos que mudar isso também em um minuto.

  • $urlé - surpresa! - o URL.

Portanto, em nosso retorno de chamada t5_update_wp_per_https(), examinamos a URL e, se for uma URL que queremos filtrar, dizemos NÃO ao WordPress por não dizer "não" ( false).

insira a descrição da imagem aqui

Nota: A seguir, você pode impedir todas as solicitações HTTP com:
add_filter( 'pre_http_request', '__return_true' );

Em vez disso, acionamos nossa própria solicitação com uma URL melhor e argumentos ligeiramente ajustados ( $rrenomeados $argspara facilitar a leitura).

O código

Por favor, leia os comentários em linha, eles são 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;
}

Os testes

Sem esse plugin, o WordPress usado:

  • http://api.wordpress.org/core/version-check/1.6/ para verificações de atualização e
  • http://wordpress.org/wordpress-3.4.2.zip para baixar os novos arquivos.

Eu testei com duas instalações locais, um único site e uma organização multi-site no Win 7. Para forçar uma atualização do conjunto de I $wp_versionem wp-includes/version.phppara 1ea versão do TwentyEleven para 1.3.

Para assistir ao tráfego de rede, usei o Wireshark : é gratuito, roda em Windows e Linux e oferece algumas ferramentas de filtro impressionantes.

Observar HTTPS é um pouco difícil: você vê apenas dados criptografados ... essa é a ideia, afinal. Para ver se meu plug-in fez o que deveria, observei primeiro o tráfego não criptografado e observei o endereço IP usado para conectar ao wordpress.org. Isso foi 72.233.56.138, às vezes 72.233.56.139.
Não é de surpreender, há um balanceador de carga e provavelmente muitas outras ferramentas, portanto não podemos confiar em um endereço IP.

Então, digitei ip.addr == 72.233.56.138a máscara de filtro, ativei o plug-in, fui wp-admin/update-core.phpe observei o tráfego no Wireshark. Linhas verdes são solicitações em texto simples - exatamente o que não queremos. As linhas vermelhas e pretas são um sinal de sucesso.

Wireshark

A verificação da atualização foi boa: encontrou as versões "mais recentes". As atualizações reais para o tema e o núcleo também foram boas. Exatamente o que eu precisava.

E ainda assim ... isso seria mais fácil se houvesse um filtro simples para o URL.

fuxia
fonte
11
+1 para /* /**/, apenas porque é genial. E (se eu pudesse) outro +1 para Charles Bronson. E então deve haver outro +1 para a explicação detalhada, comentários e capturas de tela.
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
fonte
11
Deve-se mencionar que, neste exemplo de código, na função http_api_curl_custom, a variável global $ replace_url deve ser definida como NULL após ser usada. Caso contrário, após a primeira ocorrência de "wordpress.org" no URL, todos os outros URLs serão substituídos.
precisa saber é o seguinte