URL corrispondenti a Wordpress con tilde finali


11

Mi è stato consegnato un rapporto di vulnerabilità (1) che sembra implicare che potrebbe esserci un problema di sicurezza nel modo in cui Wordpress gestisce gli URL con le seguenti tilde. Sembra che lo scanner pensi che il sito Web potrebbe offrire alcuni elenchi di directory e simili.

Sono rimasto sorpreso dal fatto che il mio sito web continuasse a pubblicare contenuti su questi diversi URL, quindi ho fatto un test installando un'istanza WP totalmente vuota, sono passato a permalink "Nome post" e ho confermato che sì, qualsiasi URL con tilde aggiunta viene ancora interpretato come l'URL senza la tilde.

In effetti, un url come questo:

https://mywordpresssite.com/my-permalink

È anche accessibile con i seguenti URL:

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

Ho cercato un po 'in giro per vedere dove WP analizza i permalink e l'ho rintracciato class-wp.phpnel parse_requestmetodo, ma non ho potuto andare molto oltre.

La mia domanda è se questo è un comportamento previsto per WP e, in tal caso, c'è un modo per disattivarlo in modo che le tilde non siano abbinate? Perché WP interpreterebbe gli URL con le tilde come un URL senza di loro?

(1) Sì, ora abbiamo visto tutti un paio di grandi hack e perdite di dati nel Regno Unito, è di nuovo quella volta in cui tutti i ragazzi della "sicurezza" fanno finta di fare la propria parte consegnandoci agli sviluppatori rapporti di scansione di 200 pagine pieno di falsi positivi e problemi generici di cui non sanno nulla nelle aspettative se leggiamo e agiamo su tale rapporto, non accadrà mai nulla di brutto.

Risposte:


13

Andiamo semplice

Se capisco bene OP, il tuo problema è che gli URL che contengono una tilde sono abbinati affatto.

Tutte le altre risposte si concentrano sul fatto che la sanificazione per la query elimina alcuni caratteri prima di eseguire la query, tuttavia si dovrebbe essere in grado di impedire che una regola di riscrittura non corrisponda in alcune circostanze.

Ed è fattibile, non molto facile, ma fattibile.

Perché corrisponde, in primo luogo?

Il motivo per cui due URL gradiscono example.com/postnamee example.com/postname~corrispondono alla stessa regola di riscrittura è perché la regola di riscrittura WP per i post utilizza il tag di riscrittura %postname%che viene sostituito da regex ([^/]+)quando vengono create le regole di riscrittura.

Il problema è che regex ([^/]+)corrisponde anche al nome post postname~e, a causa della sanificazione, il nome richiesto finirà postnamecon un risultato valido.

Ciò significa che se siamo in grado di modificare l'espressione regolare da ([^/]+)a ([^~/]+)tilde non corrisponderà più così possiamo prevenire attivamente gli URL che contengono tilde in nome posto da corrispondere.

Dal momento che nessuna regola corrisponderà, l'URL finirà per essere un 404, che dovrebbe essere il comportamento previsto, credo.

Prevenire l'abbinamento

add_rewrite_tagè una funzione che, nonostante il suo nome, può essere utilizzata per aggiornare un tag di riscrittura esistente come %postname%.

Quindi, se usiamo il codice:

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

raggiungeremo il nostro obiettivo e example.com/postname~sarà non corrispondere alla regola per example.com/postname.

Quindi, sì, le 3 righe sopra sono l'unico codice di cui avrai bisogno .

Tuttavia, prima che funzioni, devi scaricare le regole di riscrittura visitando la pagina delle impostazioni di permalink sul back-end.

Nota che regex ([^~/]+)impedisce a una tilde di essere ovunque nel nome del post, non solo come carattere finale, ma poiché i nomi dei post non possono effettivamente contenere tilde a causa della sanificazione, ciò non dovrebbe essere un problema.


1
A +1 piace la semplicità ;-) sembra anche che potremmo regolarlo anche per altri caratteri di rumore.
birgire

1
@birgire non siamo tutti? ;)
gmazzap

@birgire sì, potremmo impedire qualsiasi carattere rimosso sanitize_title, ma poiché è filtrabile, non è possibile scrivere una soluzione sempre valida. Quindi sono andato specifico.
gmazzap

1
Questa risposta ha di gran lunga la soluzione più pulita e spiega chiaramente il problema che stiamo affrontando. Grazie mille - generosità a te!
Ken

7

è un comportamento previsto per WP

Sì, come già spiegato, WP_Query::get_posts()utilizza sanitize_title_for_query()( che utilizzasanitize_title() ) per disinfettare il nome del post di un singolo post.

In breve, dopo che il nome del post è passato sanitize_title_for_query(), in my-permalink === my-permalink~~~quanto sanitize_title_for_query()rimuove il finale ~~~. È possibile verificare ciò procedendo come segue:

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

c'è un modo per disattivarlo in modo che le tilde non siano abbinate

Questo non è qualcosa che puoi spegnere. C'è un filtro in sanitize_title()chiamato sanitize_titleche puoi usare per modificare il comportamento di sanitize_title(), ma che non è quasi sempre una buona idea. L'iniezione di SQL è molto seria, quindi lasciare che qualcosa scivoli attraverso le fessure a causa di cattivi servizi igienico-sanitari può avere un'influenza davvero negativa sull'integrità del tuo sito. "Over servizi igienico-sanitari" a volte può essere un dolore al sedere.

Non sono sicuro di quello che stai cercando, ma sospetto che potresti voler 404 singoli post con queste tilde finali, nelle tue parole, "spegnilo". L'unico modo a cui riesco a pensare in questa fase è di fermare la domanda principale quando abbiamo queste tilde finali. Per questo, possiamo filtrare la posts_whereclausola della query principale.

IL FILTRO

Nota: ho considerato solo i normali post singoli e non le prime pagine statiche o gli allegati, è possibile estendere il filtro per incorporarlo

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

POCHE NOTE

Il filtro sopra restituirà una pagina 404 quando abbiamo un URL simile https://mywordpresssite.com/my-permalink~~~~~~. Puoi tuttavia, rimuovendo remove_action( 'template_redirect', 'redirect_canonical' );dal filtro, reindirizzare automaticamente la query https://mywordpresssite.com/my-permalinke visualizzare il singolo post a causa del redirect_canonical()quale è agganciato a template_redirectcui gestisce il reindirizzamento dei 404 generati da WordPress


7

Sì, sembra strano che dovremmo avere la stessa corrispondenza per:

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

e ad es

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

Perché questo è possibile, sembra essere questa parte del WP_Query::get_posts()metodo:

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

dove sanitize_title_for_query()è definito come:

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

Dovrebbe essere possibile renderlo più rigoroso con il sanitize_titlefiltro, ma potrebbe non essere una buona idea sovrascrivere l'output predefinito, basato su sanitize_title_with_dashes, che è responsabile del risanamento qui. Dovresti prendere in considerazione la creazione di un ticket invece di modificarlo, se non ci sono già una volta corrente su questo comportamento.

Aggiornare

Mi chiedo se potremmo ripulire il rumore dalla corrente del percorso sanitize_title_for_query()e reindirizzare all'URL pulito, se necessario?

Ecco una demo con cui puoi giocare sul tuo sito di test e adattarti alle tue esigenze:

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

Potrebbe anche essere meglio utilizzare sanitize_title_with_dashes()direttamente per evitare i filtri e sostituire:

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

con:

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

ps: penso di aver imparato questo trucco, per ottenere il percorso corrente con un vuoto add_query_arg( [] ), da @gmazzap ;-) Questo è anche notato nel Codice. Grazie ancora a @gmazzap per il promemoria dell'utilizzo esc_url()quando si visualizza l'output di add_query_arg( [] )o esc_url_raw()quando, ad esempio, il reindirizzamento. Controlla anche il precedente riferimento al Codice.


+1 Solo per chiarire, quei caratteri speciali vengono rimossi, quindi, sebbene la strana versione dell'URL sia visibile nella barra degli indirizzi, WordPress funziona con l'URL effettivo, motivo per cui la richiesta funziona in primo luogo. Non vedo alcun rischio per la sicurezza del sindaco con quel comportamento.
Nicolai,

1
sì, penso che non dovremmo scherzare con il filtro di risanamento per cambiare questo @ialocin
birgire

1
Certo, a meno che non ci sia una buona ragione, è una seccatura non ne vale la pena. Per non dire, molto probabilmente non va bene per il buonsenso degli sviluppatori - nemmeno entrare nel risanamento tecnico. Solo i miei due centesimi però.
Nicolai,

1
@birgire se usato in questo modo add_query_argdeve essere evitato esc_urlo esc_url_rawper prevenire problemi di sicurezza ...
gmazzap

ah sì grazie, se ricordo bene questo è stato un problema di sicurezza scoperto di recente in molti plugin @gmazzap
birgire

3

Consentitemi di spiegare l'elaborazione di una richiesta da parte di WordPress e un metodo per modificare il comportamento di WordPress per raggiungere i vostri obiettivi di conseguenza.

Analisi della richiesta

Quando WordPress riceve una richiesta, avvia un processo di dissezione della richiesta e trasformazione in una pagina. Il nucleo di questo processo inizia quando WP::main()viene chiamato il metodo di query principale di WordPress . Questa funzione analizza la query, come correttamente identificato, in parse_request()(in includes/class-wp.php). Lì, WordPress tenta di abbinare l'URL a una delle regole di riscrittura . Quando l'URL viene abbinato, crea una stringa di query delle parti URL e codifica queste parti (tutto tra due barre) usando urlencode(), per evitare che caratteri speciali come &incasinare la stringa di query. Questi caratteri codificati potrebbero averti indotto a pensare che il problema risiedesse lì, ma in realtà sono stati trasformati nei corrispondenti caratteri "reali" durante l'analisi della stringa di query.

Esecuzione della query associata alla richiesta

Dopo che WordPress ha analizzato l'URL, imposta la classe di query principale WP_Query, che viene eseguita nello stesso main()metodo della WPclasse. Il manzo di WP_Querypuò essere trovato nel suo get_posts()metodo in cui tutti gli argomenti della query vengono analizzati e disinfettati e la query SQL effettiva viene costruita (e, alla fine, eseguita).

In questo metodo, sulla riga 2730, viene eseguito il seguente codice:

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

Questo sanifica il post per recuperarlo dalla tabella dei post. L'output delle informazioni di debug all'interno del ciclo mostra che è qui che si trova il problema: il nome del tuo post my-permalink~, viene trasformato in my-permalink, che viene quindi utilizzato per recuperare il post dal database.

La funzione di sanificazione post titolo

La funzione sanitize_title_for_querychiama sanitize_titlecon i parametri corretti, che procede alla sanificazione del titolo. Ora il nucleo di questa funzione sta applicando il sanitize_titlefiltro:

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

Questo filtro è, in WordPress nativo, una singola funzione collegato ad esso: sanitize_title_with_dashes. Ho scritto un'ampia panoramica di ciò che fa questa funzione, che può essere trovata qui . In questa funzione, la linea che sta causando il tuo problema è

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

Questa riga rimuove tutti i caratteri ad eccezione di caratteri alfanumerici, spazi, trattini e caratteri di sottolineatura.

Risolvere il tuo problema

Quindi, esiste fondamentalmente un solo modo per risolvere il problema: rimuovere la sanitize_title_with_dashesfunzione dal filtro e sostituirla con la propria funzione. In realtà non è così difficile da fare, ma :

  1. Quando WordPress modifica il processo interno di sanificazione dei titoli, ciò avrà effetti importanti sul tuo sito Web.
  2. Altri plugin collegati a questo filtro potrebbero non gestire correttamente la nuova funzionalità.
  3. Ancora più importante : WordPress utilizza il risultato della sanitize_titlefunzione direttamente nella query SQL da questa riga:

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

    Se dovessi mai considerare di cambiare il filtro, assicurati di sfuggire correttamente al titolo prima che venga utilizzato nella query!

Conclusione: risolvere il problema non è necessario per quanto riguarda la sicurezza, ma se si desidera farlo, sostituirlo sanitize_title_with_dashescon la propria funzionalità e prestare attenzione all'evasione dell'SQL.

NB tutti i nomi dei file e i numeri di riga corrispondono ai file WordPress 4.4.2.


3

Alcune persone hanno già spiegato il problema, quindi posterò una soluzione alternativa. Dovrebbe essere abbastanza autoesplicativo.

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

Si dovrà fare qualcosa di un po 'diverso per i tipi di post-gerarchici, però, dal momento che WP_Queryverrà eseguito pagenameattraverso wp_basenamee poi disinfettare, in modo query_vars['pagename']e get_query_var('pagename')non corrisponderanno ai bambini taormina quest'ultimo non conterrà la parte padre.

Vorrei redirect_canonicalsolo prendermi cura di questa merda.


0

QUESTO È IL FISSO ... PER IL BUG DI WORDPRESS AGGIUNGI SOLO IL BLOCCO DI MOD mod di sicurezza sopra il BLOCCO generato da 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

-3

Puoi sempre provare ad aggiungere aggiungendo quanto segue al tuo .htaccessfile:

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

La seconda riga sopra dovrebbe andare proprio sotto la prima riga mostrata. Dovrebbe impedire la index.php~visualizzazione negli URL.


Questo non funziona per i graziosi permalink di cui parla la domanda, giusto?
Nicolai,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.