末尾のチルダを含むWordpressの一致するURL


11

Wordpressが次のチルダを使用してURLを処理する方法にセキュリティ問題がある可能性があることを示唆しているように見える脆弱性レポート(1)を受け取りました。スキャナーは、ウェブサイトがいくつかのディレクトリ一覧などを提供していると考えているようです。

私のウェブサイトがまだこれらの異なるURLでコンテンツを提供していることに驚いたので、完全に空白のWPインスタンスをインストールしてテストを行い、「投稿名」のパーマリンクに切り替えて、はい、チルドが追加されたURLがまだ次のように解釈されることを確認しましたチルダなしのURL。

確かに、このようなURL:

https://mywordpresssite.com/my-permalink

次のURLでもアクセスできます。

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

WPがパーマリンクを解析する場所を確認するために少しつついてclass-wp.phpparse_requestメソッドでそれを追跡しましたが、それ以上のことはできませんでした。

私の質問は、これがWPの意図された動作であるかどうか、そうであれば、これをオフにしてチルダが一致しないようにする方法はありますか?なぜWPはチルダのあるURLを、チルダのないURLとして解釈するのですか?

(1)はい、イギリスでいくつかの主要なハッキングとデータリークが発生しました。「セキュリティ」担当者が開発者に200ページのスキャンレポートを渡して、自分たちのビットを実行しているふりをするのは今回も同じです。誤検知や一般的な問題に満ちていて、私たちがこのレポートを読んで対処した場合、彼らは期待について何も知りません。悪いことは決して起こりません。

回答:


13

簡単に行こう

OPをよく理解している場合、問題はチルドを含むURLがまったく一致しないことです。

他のすべての回答は、クエリのサニタイズがクエリを実行する前に一部の文字を削除するという事実に焦点を当てていますが、状況によっては書き換えルールが一致しないのを防ぐことができるはずです。

そして、それは実行可能で、非常に簡単ではありませんが、実行可能です。

そもそもなぜ一致するのか?

なぜ2つのURLのような理由example.com/postnameexample.com/postname~ポストのためのWPの書き換えルールを書き換えタグを使用するので、同じ書き換えルールに一致するがある%postname%正規表現に置き換えられ([^/]+)、書き換え規則が作成されたときに。

問題は、正規表現([^/]+)がポストネームにも一致し、サニタイズのpostname~ために、クエリされた名前がpostname有効な結果になることです。

つまり、正規表現を([^/]+)から([^~/]+)チルダに変更できた場合、一致しなくなるため、投稿名にチルダを含むURLが一致することを積極的に防ぎます。

一致するルールがないため、URLは404になり、これは予想される動作であると思います。

一致を防ぐ

add_rewrite_tagその名前にもかかわらず、などの既存の書き換えタグを更新するために使用できる関数です%postname%

したがって、コードを使用する場合:

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

我々は我々の目標を達成し、example.com/postname~なりませんためのルールと一致しますexample.com/postname

だから、はい、必要なコードは上記の3行だけです

ただし、機能する前に、バックエンドのパーマリンク設定ページにアクセスして、書き換えルールをフラッシュする必要があります。

正規表現では([^~/]+)、チルダが末尾の文字としてだけでなく、投稿名のどこにも存在しないようにするだけでなく、サニタイズのために投稿名に実際にはチルダを含めることができないため、問題にはなりません。


1
+1のような単純さ;-)また、これを他のノイズ文字に対しても調整できるように見えます。
バージィ2016年

1
@birgireみんなでしょ?;)
gmazzap

@birgireはい、によって取り除かれるすべての文字を防ぐことができますsanitize_titleが、それはフィルター可能であるため、常に有効なソリューションを作成することはできません。だから私は具体的に行きました。
gmazzap

1
この答えは、はるかにクリーンなソリューションであり、私たちが直面している問題を明確に説明しています。どうもありがとうございました。
dKen

7

WPの意図された動作です

はい、すでに説明したように、をWP_Query::get_posts()使用してsanitize_title_for_query()を使用sanitize_title())、単一の投稿の投稿名をサニタイズします。

要するに、ポスト名前が通過した後sanitize_title_for_query()my-permalink === my-permalink~~~としてsanitize_title_for_query()削除し、末尾~~~。これをテストするには、次のようにします。

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

チルダが一致しないようにこれをオフにできる方法はありますか

これはオフにできるものではありません。には、の動作を変更するために使用できるsanitize_title()呼び出されたフィルターがsanitize_titleありますがsanitize_title()、これはほとんどの場合、あまり良い考えではありません。SQLインジェクションは非常に深刻であるため、不適切な衛生管理が原因で亀裂に何かが入り込むと、サイトの整合性に本当に悪影響を及ぼす可能性があります。「過剰な衛生管理」は、お尻の痛みになることがあります。

私はあなたが何をしているのかわかりませんが、おそらくあなたはおそらくこれらの末尾のチルダを含む単一の投稿を404にしたいのではないかと思います。この段階で私が考えることができる唯一の方法は、これらの末尾のチルダがあるときにメインクエリを停止することです。このためposts_where、メインクエリの句をフィルター処理できます。

フィルター

注:通常の単一の投稿のみを考慮し、静的なフロントページや添付ファイルは考慮していません。フィルターを拡張してこれを組み込むことができます

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

いくつかの注意事項

上記のフィルターは、のようなURLがある場合に404​​ページを返しますhttps://mywordpresssite.com/my-permalink~~~~~~。ただし、削除することによってすることができremove_action( 'template_redirect', 'redirect_canonical' );、フィルタから、自動的にクエリを持っているにリダイレクトhttps://mywordpresssite.com/my-permalinkして起因する単一のポストを表示するredirect_canonical()にフックしているtemplate_redirectワードプレスのハンドルのリダイレクトは404個のを生成しました


7

はい、同じように一致させる必要があるのは奇妙に見えます。

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

そして例えば

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

なぜこれが可能であるかは、メソッドのこの部分のようWP_Query::get_posts()です:

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

場所sanitize_title_for_query():のように定義されます

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

sanitize_titleフィルターを使用してこれをより厳密にすることは可能ですsanitize_title_with_dashesが、ここでの衛生管理を担当するに基づいて、デフォルトの出力をオーバーライドすることはお勧めできません 。この動作に関する最新情報がまだない場合は、チケットを変更する代わりに作成することを検討してください。

更新

現在のパスからノイズをクリーンアップして、sanitize_title_for_query()必要に応じてクリーンアップされたURLにリダイレクトできるかどうか疑問に思いますか?

テストサイトで遊んで、ニーズに合わせて調整できるデモを次に示します。

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

sanitize_title_with_dashes()フィルターを回避して置き換えるには、直接使用する方がよい場合もあります。

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

で:

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

ps:add_query_arg( [] )@gmazzapから現在のパスを空にするためのこのトリックを学んだと思います;-)これはコーデックスにも記載されています。@gmazzap esc_url()の出力を表示するとき、add_query_arg( [] )またはesc_url_raw()リダイレクトするときなどに使用することを思い出させてくれてありがとう。以前のCodexリファレンスも確認してください。


+1明確にするために、これらの特殊文字は削除されるため、URLの奇妙なバージョンがロケーションバーに表示されますが、WordPressは実際のURLで機能するため、リクエストが最初に機能します。私はその振る舞いによる市長のセキュリティリスクを見ていません。
ニコライ

1
はい、私はこの@ialocinを変更するために衛生フィルターをいじってはいけないと思います
バージリー

1
確かに、非常に正当な理由がない限り、それは価値のない手間です。言うまでもなく、それは開発者の正気に良くない可能性が高いです-技術的な衛生にさえさえ入りません。ただ私の2セント。
ニコライ

1
ので、同じように使用@birgire add_query_arg必要がでエスケープするesc_urlか、esc_url_rawセキュリティ上の問題を防ぐために...
gmazzap

おかげで、私が正しくリコール場合、これは最近@gmazzap多くのプラグインで発見されたセキュリティ上の問題だったそうああ
birgire

3

WordPressによるリクエストの処理と、それに応じてWordPressの動作を変更して目標を達成する方法について説明します。

リクエストの解析

WordPressはリクエストを受け取ると、リクエストを分析してページに変換するプロセスを開始します。このプロセスのコアは、WordPressのメインクエリメソッドWP::main()が呼び出されたときに始まります。この関数は、parse_request()(でincludes/class-wp.php)正しく識別されたとおりにクエリを解析します。そこで、WordPressはURLを書き換えルールの 1つと照合しようとします。URLが一致すると、URL部分のクエリ文字列が作成され、これらの部分(2つのスラッシュの間のすべて)がを使用してエンコードされ、クエリ文字列が混乱するurlencode()などの特殊文字が防止さ&れます。これらのエンコードされた文字により、問題はそこにあると思われるかもしれませんが、実際には、クエリ文字列を解析するときに対応する「実際の」文字に変換されます。

リクエストに関連付けられたクエリを実行する

WordPressがURLを解析した後、クラスのWP_Query同じmain()メソッドで実行されるメインクエリクラスを設定しますWP。牛肉は、WP_Queryその中に見つけることができるget_posts()すべてのクエリの引数を解析し、消毒し、実際のSQLクエリを構築(及び、最終的には、実行)されている方法。

このメソッドでは、行2730で次のコードが実行されます。

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

これは、postsテーブルから取得するために投稿をサニタイズします。ループ内でデバッグ情報を出力すると、これが問題の原因であることがわかります。投稿名my-permalink~はに変換さmy-permalinkれ、データベースから投稿を取得するために使用されます。

投稿タイトル消毒機能

関数sanitize_title_for_querysanitize_title適切なパラメーターを指定して呼び出し、タイトルをサニタイズします。この関数のコアはsanitize_titleフィルターを適用することです:

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

このフィルターには、ネイティブのWordPressでは1つの関数が付加されていますsanitize_title_with_dashes。私はこの関数が何をするかの概要を書きました、それはここにありますこの関数では、問題の原因となっている行は

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

この行は、英数字、スペース、ハイフン、およびアンダースコアを除くすべての文字を取り除きます。

問題を解決する

したがって、問題を解決する方法は基本的に1つsanitize_title_with_dashesです。フィルターから関数を削除し、独自の関数に置き換えることです。これは実際にはそれほど難しくありませんが、

  1. WordPressがタイトルをサニタイズする内部プロセスを変更すると、これはWebサイトに大きな影響を与えます。
  2. このフィルターにフックする他のプラグインは、新しい機能を正しく処理しない可能性があります。
  3. 最も重要なのは、WordPressがsanitize_title関数の結果を次の行のSQLクエリで直接使用することです。

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

    フィルターの変更を検討する場合は、クエリで使用する前にタイトルを適切にエスケープしてください。

結論:セキュリティに関する限り、問題を解決する必要はありませんが、必要な場合はsanitize_title_with_dashes、独自の機能に置き換えて、SQLエスケープに注意してください。

すべてのファイル名と行番号はWordPress 4.4.2ファイルに対応しています。


3

一部の人々はすでに問題を説明しているので、代替ソリューションを投稿します。自明である必要があります。

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

あなたは以来、にもかかわらず階層ポストタイプに対してビット別の何かを行う必要がありますWP_Query実行されpagenamewp_basename、それを消毒するため、query_vars['pagename']そしてget_query_var('pagename')後者の意志becuase子供が親部品が含まれていないため一致しません。

redirect_canonicalはこのがらくたの世話をしたいと思います。


0

これが修正です... WORDPRESSのバグの場合、Wordpressで生成されたブロックの上にBEGINセキュリティmodブロックを追加するだけです。

# 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

.htaccessファイルに以下を追加して、いつでも追加してみることができます。

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

上記の2行目は、表示されている最初の行のすぐ下にあります。index.php~URLに表示されないようにする必要があります。


これは、質問に関するかなりのパーマリンクでは機能しません。
ニコライ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.