URLs coincidentes de Wordpress con tildes finales

11

Me han entregado un informe de vulnerabilidad (1) que parece implicar que puede haber un problema de seguridad en la forma en que Wordpress maneja las URL con los siguientes tildes. Parece que el escáner piensa que el sitio web puede ofrecer algunos listados de directorios y demás.

Me sorprendió que mi sitio web todavía estuviera mostrando contenido en esas URL diferentes, así que hice una prueba instalando una instancia de WP totalmente en blanco, cambié a "Nombre del mensaje", y confirmé que sí, cualquier URL con tilde adicional aún se interpreta como la URL sin la tilde.

De hecho, una url como esta:

https://mywordpresssite.com/my-permalink

También se puede acceder con las siguientes URL:

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

Busqué un poco para ver dónde WP analiza los enlaces permanentes, y lo localicé en class-wp.php en el método parse_request , pero no pude llegar mucho más lejos.

Mi pregunta es si este es el comportamiento previsto de WP, y si es así, ¿hay alguna manera de que pueda desactivarlo para que no coincidan los tildes? ¿Por qué WP interpretaría las URL con tildes como una URL sin ellas?

(1) Sí, ahora hemos visto un par de hacks importantes y fugas de datos en el Reino Unido, es el momento en el que los chicos de "seguridad" todos fingen que están haciendo su gran esfuerzo al entregarnos Los desarrolladores de informes de 200 páginas están llenos de falsos positivos y problemas genéricos de los que no saben nada con la expectativa de que si leemos y actuamos en dicho informe, nunca sucederá nada malo.

    
pregunta dKen 15.11.2015 - 18:19

7 respuestas

13

Vayamos simples

Si entiendo bien a OP, su problema es que las URL que contienen una tilde se comparan en absoluto.

Todas las demás respuestas se centran en el hecho de que la limpieza de la consulta elimina algunos caracteres antes de realizar la consulta, sin embargo, una debería ser capaz de evitar que una regla de reescritura no coincida en algunas circunstancias.

Y es factible, no muy fácil, pero factible.

¿Por qué coincide, en primer lugar?

La razón por la que dos URL como example.com/postname y example.com/postname~ coinciden con la misma regla de reescritura es porque la regla de reescritura de WP para las publicaciones usa la etiqueta de reescritura %postname% que se reemplaza por la expresión regular ([^/]+) cuando se crean reglas de reescritura.

El problema es que regex ([^/]+) también coincide con el postname postname~ y, debido a la limpieza, el nombre consultado será postname y obtendrá un resultado válido.

Esto significa que si podemos cambiar la expresión regular de ([^/]+) a ([^~/]+) tilde ya no coincidirá, por lo que evitamos activamente que las URL que contienen tilde en el nombre de la publicación coincidan.

Como ninguna regla coincidirá, la url terminará siendo un 404, creo que debería ser el comportamiento esperado.

Evitar coincidencias

add_rewrite_tag es una función que, a pesar de su nombre, se puede usar para actualizar una etiqueta de reescritura existente como %postname% .

Entonces, si usamos el código:

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

alcanzaremos nuestro objetivo y example.com/postname~ no coincidirá con la regla para example.com/postname .

Entonces, sí, las 3 líneas anteriores son el único código que necesitarás .

Sin embargo, antes de que funcione, deberá vaciar las reglas de reescritura visitando la página de configuración de enlace permanente en el servidor.

Tenga en cuenta que las expresiones regulares ([^~/]+) impiden que una tilde esté en cualquier lugar del nombre de la publicación, no solo como carácter final, sino que, dado que los nombres de las publicaciones no pueden contener tilde debido a un saneamiento, eso no debería ser un problema.

    
respondido por el gmazzap 30.03.2016 - 20:01
7
  

es el comportamiento previsto para WP

Sí, como ya se explicó, WP_Query::get_posts() utiliza sanitize_title_for_query() ( que utiliza sanitize_title() ) para sanear el nombre de la publicación de una publicación en singular.

En resumen, después de que el nombre de la publicación haya pasado por sanitize_title_for_query() , my-permalink === my-permalink~~~ as sanitize_title_for_query() elimina el ~~~ final. Puedes probar esto haciendo lo siguiente:

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

hay alguna forma en que pueda apagarlo para que los tildes no coincidan

Esto no es algo que puedas apagar. Hay un filtro en sanitize_title() llamado sanitize_title que puede usar para alterar el comportamiento de sanitize_title() , pero eso casi siempre no es una muy buena idea. La inyección de SQL es muy grave, por lo que dejar que algo se escape a través de las grietas debido a un mal saneamiento puede tener una mala influencia en la integridad de su sitio. "Sobre el saneamiento" a veces puede ser un dolor en el trasero.

No estoy seguro de qué es lo que está buscando, pero sospecho que quizás quiera 404 publicaciones individuales con estas tilde finales, en sus palabras, "apáguelo". La única forma en la que puedo pensar en esta etapa es detener la consulta principal cuando tenemos estos tildes finales. Para esto, podemos filtrar la cláusula posts_where de la consulta principal.

EL FILTRO

Nota: solo consideré publicaciones singulares normales, y no páginas frontales o archivos adjuntos estáticos, puedes ampliar el filtro para incorporar esto

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

ALGUNAS NOTAS

El filtro anterior devolverá una página 404 cuando tengamos una URL como https://mywordpresssite.com/my-permalink~~~~~~ . Sin embargo, puede eliminar remove_action( 'template_redirect', 'redirect_canonical' ); del filtro, redirigir automáticamente la consulta a https://mywordpresssite.com/my-permalink y mostrar la publicación única debida a redirect_canonical() que se enlaza a template_redirect que maneja la redirección de las 404's generadas por WordPress

    
respondido por el Pieter Goosen 29.03.2016 - 22:46
7

Sí, parece extraño que tengamos la misma coincidencia para:

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

y, por ejemplo,

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

¿Por qué esto es posible, parece ser > del método WP_Query::get_posts() :

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

donde sanitize_title_for_query() se define como:

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

Debería ser posible hacer esto más estricto con el filtro sanitize_title , pero podría no ser una buena idea anular la salida predeterminada, basada en sanitize_title_with_dashes , que es responsable del saneamiento aquí. Debería considerar crear un ticket en lugar de cambiarlo, si no hay una vez, una vez, acerca de este comportamiento.

Actualizar

Me pregunto si podríamos limpiar el ruido de la ruta actual con sanitize_title_for_query() y redirigir a la url limpia si es necesario.

Aquí hay una demostración que puede jugar en su sitio de prueba y ajustarse a sus necesidades:

/**
 * 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;
} );

Incluso podría ser mejor usar sanitize_title_with_dashes() directamente para evitar los filtros y reemplazar:

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

con:

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

ps: creo que aprendí este truco para obtener la ruta actual con un add_query_arg( [] ) vacío, de @gmazzap ;-) También es señalado en el Códice. Gracias de nuevo a @gmazzap por el recordatorio de usar esc_url() cuando se muestra la salida de add_query_arg( [] ) o esc_url_raw() cuando, por ejemplo. redirigiéndolo. Verifique la referencia previa del Codex para eso también.

    
respondido por el birgire 29.03.2016 - 15:28
3

Permítame explicarle el procesamiento de WordPress de una solicitud y un método para cambiar el comportamiento de WordPress para lograr sus objetivos en consecuencia.

Analizar la solicitud

Cuando WordPress recibe una solicitud, comienza un proceso de disección de la solicitud y la transformación en una página. El núcleo de este proceso comienza cuando se llama al método de consulta principal de WordPress WP::main() . Esta función analiza la consulta, como se identificó correctamente, en parse_request() (en includes/class-wp.php ). Allí, WordPress intenta hacer coincidir la URL con una de las reglas de reescritura . Cuando la URL coincide, crea una cadena de consulta de las partes de la URL y codifica estas partes (todo entre dos barras) usando urlencode() , para evitar que los caracteres especiales como & ensucien la cadena de consulta. Es posible que estos caracteres codificados le hayan hecho pensar que el problema residía allí, pero en realidad se convierten en sus caracteres "reales" correspondientes al analizar la cadena de consulta.

Ejecutando la consulta asociada con la solicitud

Después de que WordPress haya analizado la URL, configura la clase de consulta principal, WP_Query , que se realiza en el mismo método main() de la clase WP . La base de WP_Query se puede encontrar en su método get_posts() , donde todos los argumentos de consulta se analizan y sanean y la consulta SQL real se construye (y, finalmente, se ejecuta).

En este método, en la línea 2730, se ejecuta el siguiente código:

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

Esto desinfecta la publicación para recuperarla de la tabla de publicaciones. La información de depuración de salida dentro del bucle muestra que aquí es donde reside el problema: su nombre de publicación, my-permalink~ , se transforma en my-permalink , que luego se usa para recuperar la publicación de la base de datos.

La función de desinfección del título de la entrada

La función sanitize_title_for_query llama a sanitize_title con los parámetros adecuados, que procede a desinfectar el título. Ahora, el núcleo de esta función es aplicar el filtro sanitize_title :

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

Este filtro tiene, en WordPress nativo, una única función asociada: sanitize_title_with_dashes . He escrito una amplia descripción de lo que hace esta función, que se puede encontrar aquí . En esta función, la línea que está causando tu problema es

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

Esta línea elimina todos los caracteres excepto los caracteres alfanuméricos, espacios, guiones y guiones bajos.

Resolviendo tu problema

Entonces, básicamente hay una sola manera de resolver su problema: eliminar la función sanitize_title_with_dashes del filtro y reemplazarla con su propia función. En realidad, esto no es tan difícil de hacer, pero :

  1. Cuando WordPress cambia el proceso interno de desinfección de títulos, esto tendrá efectos importantes en su sitio web.
  2. Es posible que otros complementos que se enganchen en este filtro no manejen correctamente la nueva funcionalidad.
  3. Lo más importante : WordPress usa el resultado de la función sanitize_title directamente en la consulta SQL en esta línea:

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

    Si alguna vez consideras cambiar el filtro, ¡asegúrate de escapar correctamente del título antes de que se use en la consulta!

Conclusión: la solución de su problema no es necesaria en lo que respecta a la seguridad, pero si desea hacerlo, reemplace sanitize_title_with_dashes con su propia funcionalidad y preste atención al escape de SQL.

NB: todos los nombres de archivos y números de línea se corresponden con los archivos de WordPress 4.4.2.

    
respondido por el engelen 29.03.2016 - 15:38
3

Algunas personas ya han explicado el problema, así que solo publicaré una solución alternativa. Debería ser bastante 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.
    }
});

Sin embargo, tendrás que hacer algo un poco diferente para los tipos de publicaciones jerárquicas, ya que WP_Query ejecutará pagename a través de wp_basename y luego lo desinfectará, por lo que query_vars['pagename'] y get_query_var('pagename') no coincidirán con los niños Debido a que este último no contendrá la parte principal.

Ojalá redirect_canonical se haya ocupado de esta mierda.

    
respondido por el kovshenin 02.04.2016 - 14:06
0

ESTE ES EL ARREGLO ... PARA EL ERROR DE WORDPRESS SOLO AGREGUE EL bloque de MOD de seguridad BEGIN sobre el BLOQUE generado por 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
    
respondido por el Michael S. Howard 08.03.2018 - 13:52
-3

Siempre puedes intentar agregar lo siguiente a tu archivo .htaccess :

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

La segunda línea de arriba debe ir justo debajo de la primera línea que se muestra. Debería evitar que index.php~ se muestre en las URL.

    
respondido por el Cutter 29.03.2016 - 15:39

Lea otras preguntas en las etiquetas