問題描述
我已經提交了一個漏洞報告 (1),這似乎意味著 Wordpress 處理具有以下波浪符的 URL 的方式可能會出現安全問題。看起來掃描器認為網站可能正在為某些目錄列表提供服務。
我很驚訝,我的網站仍然在這些不同的網址上提供內容,所以我做了一個測試,安裝一個完全空白的 WP 例項,切換到”Post name” 永久連結,並確認,任何帶有新增波浪號的 URL 仍然被解釋為沒有波浪號
的確,這樣一個網址:
https://mywordpresssite.com/my-permalink
也可透過以下 URL 訪問:
https://mywordpresssite.com/my-permalink~
https://mywordpresssite.com/my-permalink~/
https://mywordpresssite.com/my-permalink~~~~~~
我戳了一下,看到 WP 解析固定連結的位置,並且我將其按 parse_request 方法跟蹤到 class-wp.php,但不能比這更遠。
我的問題是,如果這是針對 WP 的行為,如果是,有什麼辦法可以關閉它,所以波浪號不匹配?為什麼 WP 將波紋符號的 URL 解釋為沒有它們的 URL?
(1) 是的,現在我們都看到了英國的幾個主要的駭客和資料洩露,現在再次,”security” 的所有人都假裝他們透過向我們開發人員提供 200 頁掃描報告, false-positives 和一般性問題,如果我們閱讀並對所述報告採取行動,他們就不會在期望中知道什麼,沒有什麼不好的。
最佳解決方案
我們去簡單吧
如果我理解 OP 好,你的問題是包含波浪號的網址完全匹配。
所有其他答案的重點在於,在執行查詢之前,查詢的清理消除了一些字元,但是在某些情況下,應該能夠防止重寫規則不匹配。
它是可行的,不是很容易,但可行。
為什麼匹配,首先?
為什麼像 example.com/postname 和 example.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 行是你需要的唯一程式碼。
但是,在它工作之前,您需要重新整理重寫規則,方法是訪問後端的固定連結設定頁面。
請注意,正規表示式 ([^~/]+)防止波浪號在郵件名稱中的任何位置,不僅作為尾隨字元,而是因為釋出名稱因為清理而不能實際包含波浪號,那應該不是問題。
次佳解決方案
是的,我們應該有一樣的匹配:
example.tld/2016/03/29/test/
和例如
example.tld/2016/03/29/..!!$$~~test~~!!$$../
為什麼這是可能的,似乎是 this part 的 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,這是負責這裡的衛生設計,可能不是一個好主意來覆蓋預設輸出。您應該考慮建立一張票,而不是改變它,如果沒有一次關於這個行為的現在。
Update
我想知道我們是否可以使用 sanitize_title_for_query()清除當前路徑中的噪音,如有必要,重定向到清理的網址?
這是一個演示,您可以在測試網站上玩,並根據您的需要進行調整:
/**
* 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:我認為我學到了這個技巧,從 @gmazzap ;-) 得到一個空的 add_query_arg( [] )的當前路徑,這也是 Codex 中的 noted 。再次感謝 @gmazzap 在顯示 add_query_arg( [] )或 esc_url_raw()的輸出時使用 esc_url()的提醒。重定向檢查以前的法典參考文獻。
第三種解決方案
is intended behaviour for 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~~~' )
is there any way I can switch this off so tildes are not matched
這不是你可以關閉的東西。 sanitize_title()中有一個稱為 sanitize_title 的過濾器,您可以使用它來更改 sanitize_title()的行為,但這幾乎總是不是一個好主意。 SQL 隱碼攻擊是非常嚴重的,因為由於衛生條件不好而導致的漏洞可能會對您網站的完整性造成很大的影響。 “Over sanitation” 有時可能是屁股的痛苦。
我不知道你是什麼,但我懷疑你可能想要 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 );
幾點注意
當我們有一個類似 https://mywordpresssite.com/my-permalink~~~~~~的 URL 時,上述過濾器將返回一個 404 頁面。然而,您可以透過從過濾器中刪除 remove_action( 'template_redirect', 'redirect_canonical' );,將查詢自動重定向到 https://mywordpresssite.com/my-permalink,並顯示單個帖子,因為 redirect_canonical()掛接到 template_redirect,它處理 WordPress 生成的 404 的重定向
參考文獻
注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。