Wordpress URLs correspondentes com tigos à direita

11

Recebi um relatório de vulnerabilidade (1) que parece estar sugerindo que pode haver um problema de segurança na maneira como o Wordpress lida com URLs com os seguintes tis. Parece que o scanner acha que o site pode estar exibindo algumas listas de diretórios e coisas do tipo.

Fiquei surpreso que meu site ainda estava veiculando conteúdo nessas diferentes URLs, então fiz um teste instalando uma instância WP totalmente em branco, mudei para permalinks "Post name" e confirmei que sim, qualquer URL com til adicionado ainda é interpretado como o URL sem o til.

De fato, uma URL como esta:

https://mywordpresssite.com/my-permalink

Também está acessível com os seguintes URLs:

https://mywordpresssite.com/my-permalink~
https://mywordpresssite.com/my-permalink~/
https://mywordpresssite.com/my-permalink~~~~~~

Eu procurei um pouco para ver onde o WP analisa os permalinks e o rastreei até class-wp.php no método parse_request , mas não consegui ir muito além disso.

A minha pergunta é se este é o comportamento pretendido para o WP e, em caso afirmativo, há alguma maneira de eu poder desativá-lo para que os tiles não sejam correspondidos? Por que o WP interpretaria os URLs com o tils como um URL sem eles?

(1) Sim, agora todos nós vimos alguns grandes hacks e vazamentos de dados no Reino Unido, é nessa hora que os caras de "segurança" fingem que estão fazendo sua parte nos entregando Os desenvolvedores têm relatórios de varredura de 200 páginas cheios de falsos positivos e questões genéricas sobre as quais eles não sabem nada na expectativa, se lermos e agirmos sobre o referido relatório, nada de ruim acontecerá.

    
por dKen 15.11.2015 / 18:19

7 respostas

13

Vamos simplificar

Se eu entendi bem o OP, seu problema é que os URLs contendo um til são correspondidos.

Todas as outras respostas se concentram no fato de que a higienização para a consulta retira alguns caracteres antes de executar a consulta, no entanto, um deve ser capaz de impedir que uma regra de reconfiguração não corresponda em algumas circunstâncias.

E é factível, não é muito fácil, mas factível.

Por que combina, em primeiro lugar?

A razão pela qual dois URLs como example.com/postname e example.com/postname~ correspondem à mesma regra de reescrita é porque a regra de reescrever WP para postagens usa a tag de reescrita %postname% que é substituído pelo regex ([^/]+) quando regras de reescrita são criadas.

O problema é que regex ([^/]+) também corresponde ao nome do post postname~ e, devido à sanitização, o nome consultado será postname terminando em um resultado válido.

Isso significa que, se pudermos alterar a regex de ([^/]+) para ([^~/]+) tilde, a correspondência não será mais compatível, por isso, impediremos que os URLs contendo o til no nome do post sejam correspondidos.

Como nenhuma regra vai corresponder, o URL será um 404, que deve ser o comportamento esperado, eu acho.

Evitar correspondência

add_rewrite_tag é uma função que, apesar de seu nome, pode ser usada para atualizar uma tag de reescrita existente como %postname% .

Então, se usarmos o código:

add_action('init', function() {
  add_rewrite_tag( '%postname%', '([^~/]+)', 'name=' );
});

atingiremos nossa meta e example.com/postname~ não corresponderá à regra de example.com/postname .

Então, sim, as três linhas acima são o único código que você precisará .

No entanto, antes de funcionar, você precisará liberar as regras de reescrita visitando a página de configurações de permalink no back-end.

Observe que regex ([^~/]+) impede que um til esteja em qualquer lugar no nome do post, não apenas como caractere final, mas como os nomes dos posts não podem conter til por causa da limpeza, isso não deve ser um problema.

    
por gmazzap 30.03.2016 / 20:01
7
  

é o comportamento pretendido para WP

Sim, conforme explicado anteriormente, WP_Query::get_posts() usa sanitize_title_for_query() ( que usa sanitize_title() ) para higienizar o nome do post de um post singular.

Em resumo, depois que o nome do post passou por sanitize_title_for_query() , my-permalink === my-permalink~~~ as sanitize_title_for_query() remove o ~~~ . Você pode testar isso fazendo o seguinte:

echo  sanitize_title_for_query( 'my-permalink~~~' )
  

existe alguma maneira de eu poder desativá-lo para que os tildes não sejam correspondidos

Isso não é algo que você possa desativar. Existe um filtro em sanitize_title() chamado sanitize_title que você pode usar para alterar o comportamento de sanitize_title() , mas isso quase sempre não é uma boa ideia. A injeção de SQL é muito séria, então deixar algo escapar pelas rachaduras devido ao mau saneamento pode ter uma influência muito negativa na integridade do seu site. "Sobre o saneamento" às vezes pode ser uma dor na bunda.

Não sei bem o que você quer, mas suspeito que você queira talvez 404 postagens únicas com esse til à direita, em suas palavras, "desligue-o". A única maneira que posso pensar neste estágio é interromper a consulta principal quando tivermos esses tils à direita. Para isso, podemos filtrar a cláusula posts_where da consulta principal.

O FILTRO

Nota: Eu só considerei posts singulares no singular, e não páginas estáticas ou anexos, você pode estender o filtro para incorporar isso

add_filter( 'posts_where', function ( $where, \WP_Query $q )
{
    // Only apply the filter on the main query
    if ( !$q->is_main_query() )
        return $where;

    // Only apply the filter on singular posts
    if ( !$q->is_singular() )
        return $where;

    // We are on a singular page, lets get the singular post name
    $name = sanitize_title_for_query( $q->query_vars['name'] );

    // Suppose $name is empty, like on ugly permalinks, lets bail and let WorPress handle it from here
    if ( !$name )
        return $where;

    // Get the single post URL
    $single_post_url = home_url( add_query_arg( [] ) );
    $parsed_url      = parse_url( $single_post_url );

    // Explode the url and return the page name from the path
    $exploded_pieces = explode( '/',  $parsed_url['path'] );
    $exploded_pieces = array_reverse( $exploded_pieces );

    // Loop through the pieces and return the part holding the pagename
    $raw_name = '';
    foreach ( $exploded_pieces as $piece ) {
        if ( false !== strpos( $piece, $name ) ) {
            $raw_name = $piece;

            break;
        }
    }

    // If $raw_name is empty, we have a serious stuff-up, lets bail and let WordPress handle this mess
    if ( !$raw_name )
        return $where;

    /**
     * All we need to do now is to match $name against $raw_name. If these two don't match,
     * we most probably have some extra crap in the post name/URL. We need to 404, even if the
     * the sanitized version of $raw_name would match $name. 
     */
    if ( $raw_name === $name )
        return $where;

    // $raw_name !== $name, lets halt the main query and 404
    $where .= " AND 0=1 ";

    // Remove the redirect_canonical action so we do not get redirected to the correct URL due to the 404
    remove_action( 'template_redirect', 'redirect_canonical' );

    return $where;
}, 10, 2 );

POUCAS NOTAS

O filtro acima retornará uma página 404 quando tivermos um URL como https://mywordpresssite.com/my-permalink~~~~~~ . Você pode, no entanto, removendo remove_action( 'template_redirect', 'redirect_canonical' ); do filtro, fazer com que a consulta redirecione automaticamente para https://mywordpresssite.com/my-permalink e exibir a postagem única devido a redirect_canonical() , que está ligado a template_redirect , que manipula o redirecionamento de 404s gerados pelo WordPress

    
por Pieter Goosen 29.03.2016 / 22:46
7

Sim, parece estranho que tenhamos a mesma correspondência para:

example.tld/2016/03/29/test/

e, por exemplo,

example.tld/2016/03/29/..!!$$~~test~~!!$$../

Por que isso é possível, parece ser esta parte do método WP_Query::get_posts() :

if ( '' != $q['name'] ) {
    $q['name'] = sanitize_title_for_query( $q['name'] );

em que sanitize_title_for_query() é definido como:

function sanitize_title_for_query( $title ) {
        return sanitize_title( $title, '', 'query' );
}

Deve ser possível tornar isso mais estrito com o filtro sanitize_title , mas pode não ser uma boa ideia substituir a saída padrão, com base em sanitize_title_with_dashes , que é responsável pelo saneamento aqui. Você deve considerar criar um ticket em vez de alterá-lo, se já não houver uma corrente sobre esse comportamento.

Atualizar

Gostaria de saber se poderíamos limpar o ruído do caminho atual com sanitize_title_for_query() e redirecionar para o URL limpo, se necessário?

Aqui está uma demonstração com a qual você pode jogar no seu site de teste e ajustar-se às suas necessidades:

/**
 * DEMO: Remove noise from url and redirect to the cleaned version if needed 
 */
add_action( 'init', function( )
{
    // Only for the front-end
    if( is_admin() )
        return;

    // Get current url
    $url = home_url( add_query_arg( [] ) );

    // Let's clean the current path with sanitize_title_for_query()
    $parse = parse_url( $url );
    $parts = explode( '/',  $parse['path'] );
    $parts = array_map( 'sanitize_title_for_query', $parts );   
    $path_clean = join( '/', $parts );
    $url_clean = home_url( $path_clean );
    if( ! empty( $parse['query'] ) )
        $url_clean .= '?' . $parse['query'];

    // Only redirect if the current url is noisy
    if( $url === $url_clean )
        return;
    wp_safe_redirect( esc_url_raw( $url_clean ) );
    exit;
} );

Pode até ser melhor usar sanitize_title_with_dashes() diretamente para evitar os filtros e substituir:

$parts = array_map( 'sanitize_title_for_query', $parts );

com:

foreach( $parts as &$part )
{
    $part = sanitize_title_with_dashes( $part, '', 'query' );
}

ps: Acho que aprendi esse truque, para obter o caminho atual com um add_query_arg( [] ) vazio, do @gmazzap ;-) Isso também é anotou no Codex. Obrigado novamente ao @gmazzap pelo lembrete de usar esc_url() ao exibir a saída de add_query_arg( [] ) ou esc_url_raw() quando, por exemplo, redirecionando-o. Verifique a referência anterior do Codex para isso também.

    
por birgire 29.03.2016 / 15:28
3

Deixe-me explicar o processamento de uma solicitação pelo WordPress e um método para mudar o comportamento do WordPress para atingir suas metas de acordo.

Analisando a solicitação

Quando o WordPress recebe uma solicitação, ele inicia um processo de dissecar a solicitação e transformá-la em uma página. O núcleo desse processo começa quando o método de consulta principal do WordPress WP::main() é chamado. Essa função analisa a consulta, como você identificou corretamente, em parse_request() (em includes/class-wp.php ). Lá, o WordPress tenta comparar o URL com uma das regras de reescrita . Quando a URL é correspondida, ela cria uma string de consulta das partes do URL e codifica essas partes (tudo entre duas barras) usando urlencode() , para evitar que caracteres especiais, como & , atrapalhem a string de consulta. Esses caracteres codificados podem ter levado você a pensar que o problema residia lá, mas na verdade eles são transformados em caracteres "reais" correspondentes ao analisar a string de consulta.

Executando a consulta associada à solicitação

Depois que o WordPress analisa o URL, ele configura a classe de consulta principal, WP_Query , que é feita no mesmo método main() da classe WP . O bife de WP_Query pode ser encontrado em seu método get_posts() , em que todos os argumentos de consulta são analisados e higienizados, e a consulta SQL real é construída (e, eventualmente, executada).

Neste método, na linha 2730, o seguinte código é executado:

$q['name'] = sanitize_title_for_query( $q['name'] );

Isso higieniza a postagem para buscá-la na tabela de postagens. A geração de informações de depuração dentro do loop mostra que esse é o lugar onde o problema reside: seu nome de postagem, my-permalink~ , é transformado em my-permalink , que é usado para buscar a postagem do banco de dados.

A função de sanitização do título da postagem

A função sanitize_title_for_query chama sanitize_title com os parâmetros apropriados, que continua a limpar o título. Agora, o núcleo dessa função é aplicar o filtro sanitize_title :

$title = apply_filters( 'sanitize_title', $title, $raw_title, $context );

Este filtro tem, no WordPress nativo, uma única função anexada a ele: sanitize_title_with_dashes . Escrevi uma visão abrangente do que essa função faz, que pode ser encontrada aqui . Nesta função, a linha que está causando o seu problema é

$title = preg_replace('/[^%a-z0-9 _-]/', '', $title);

Esta linha remove todos os caracteres, exceto caracteres alfanuméricos, espaços, hífens e sublinhados.

Resolvendo seu problema

Portanto, há basicamente uma única maneira de resolver seu problema: remover a função sanitize_title_with_dashes do filtro e substituí-la por sua própria função. Isso na verdade não é tão difícil de fazer, mas :

  1. Quando o WordPress altera o processo interno de limpeza de títulos, isso terá efeitos importantes no seu site.
  2. Outros plugins conectados a este filtro podem não manipular corretamente a nova funcionalidade.
  3. O mais importante : o WordPress usa o resultado da função sanitize_title diretamente na consulta SQL por esta linha:

    $where .= " AND $wpdb->posts.post_name = '" . $q['name'] . "'";
    

    Se você pensar em mudar o filtro, certifique-se de ter escapado do título antes de ser usado na consulta!

Conclusão: resolver o seu problema não é necessário no que diz respeito à segurança, mas se você quiser, substitua o sanitize_title_with_dashes pela sua própria funcionalidade e preste atenção no escape do SQL.

NB, todos os nomes de arquivo e números de linha correspondem aos arquivos do WordPress 4.4.2.

    
por engelen 29.03.2016 / 15:38
3

Algumas pessoas já explicaram o problema, por isso vou postar uma solução alternativa. Deve ser bem autoexplicativo.

add_action( 'template_redirect', function() {
    global $wp;

    if ( ! is_singular() || empty( $wp->query_vars['name'] ) )
        return;

    if ( $wp->query_vars['name'] != get_query_var( 'name' ) ) {
        die( wp_redirect( get_permalink(), 301 ) );
        // or 404, or 403, or whatever you want.
    }
});

No entanto, você terá que fazer algo um pouco diferente para os tipos de postagens hierárquicas, pois WP_Query executará pagename a wp_basename e, depois, sanitize, por isso query_vars['pagename'] e get_query_var('pagename') não serão compatíveis porque o último não conterá a parte pai.

Eu gostaria que redirect_canonical apenas cuidasse dessa porcaria.

    
por kovshenin 02.04.2016 / 14:06
0

ESTE É O FIX ... O ERRO DO WORDPRESS APENAS ADICIONA O bloco mod de segurança BEGIN acima do BLOCO Gerado no Wordpress.

# BEGIN security mod
<IfModule mod_rewrite.c>
RewriteRule ^.*[~]+.*$ - [R=404]
</IfModule>
#END security mod

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /wordpress/
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /wordpress/index.php [L]
</IfModule>

# END WordPress
    
por Michael S. Howard 08.03.2018 / 13:52
-3

Você pode sempre tentar adicionar o seguinte ao seu arquivo .htaccess :

RewriteEngine On
RewriteRule \.php~$ – [forbidden,last]

A segunda linha acima deve ir para a direita sob a primeira linha mostrada. Isso deve impedir que index.php~ seja exibido em URLs.

    
por Cutter 29.03.2016 / 15:39

Tags