WordPressではXML-RPCの機能がデフォルトでは有効になっています。
最新のWordPressのバージョンでは、XML-RPCに取って代わるREST APIが機能として存在するため、用途はほぼ皆無であると思います。
「WordPress xmlrpc」とググると、ブルートフォースアタックなどの脆弱性の温床になりかねないということで、無効化する方法が出てきます。
公式プラグインの WPScan ではXML-RPCが有効であるかどうかの検証をしていますが、その検証の仕方が予期しないもので、対応がなかなかうまく行かなかった話を紹介します。
結論から言うと、Webサーバーの設定でxmlrpc.phpへのアクセスを拒否する方法が簡単です。
WPScanとは
WPScanはプラグインやテーマ、WordPressのコアの脆弱性を検出するプラグインです。
コード以外にも重要なファイルがpublicになっていないか、XML-RPCが有効になっていないかを検証してくれます。
スキャンをするためには、WPScanのAPIキーが必要となります。
プランによって一日にできるスキャンの回数が変わりますが、無料版でも十分使えます。
XML-RPC機能の無効化
冒頭で説明した通り、WordPressのXML-RPC機能は用途が無い上に脆弱性をはらんでいるので無効化することに越したことは無いです。
実際に無効化する良くある方法としては、
- Webサーバーでアクセスを拒否する
- hookでXML-RPCの有効フラグを無効に変更する
という2パターンが主流かと思います。
Webサーバーでアクセスを拒否する
nginxの場合は以下のような記述を追加するだけでOKです。
location = /xmlrpc.php {
deny all;
}
hookでXML-RPCの有効フラグを無効に変更する
XML-RPCの有効フラグを上書きできるfilterが用意されています。
プラグインに以下の記述を追加してあげてください。
add_filter( 'xmlrpc_enabled', '__return_false' );
WPScanによるXML-RPCの検証方法
WPScanではXML-RPCが有効かどうかを3段階で検証しています。
ここからはWPScanの中身を見ていきます。
XML-RPCのエンドポイントが存在することを確認する
まず、XML-RPCのエンドポイントにGETリクエストをしてエンドポイントが存在しているかどうかを確認しています。
$url = get_site_url() . '/xmlrpc.php';
// First check if the xmlrpc.php file returns a 405 code.
$is_available = wp_remote_get( $url, array( 'timeout' => 5 ) );
$is_available_code = wp_remote_retrieve_response_code( $is_available );
if ( 405 !== $is_available_code ) return;
レスポンスコードが405以外だった場合は無効化されていると判断して、そのまま返却されます。
Webサーバーでアクセス拒否にした場合は、この時点で無効になっていると判定されます。
hookで上書きの場合は、GETアクセスに対しては何も手当をしていないので次の処理に進みます。
ログインを試みる
// Try an authenticated request.
$authenticated_body = '<?xml version="1.0" encoding="iso-8859-1"?><methodCall><methodName>wp.getUsers</methodName><params><param><value>1</value></param><param><value>username</value></param><param><value>password</value></param></params></methodCall>';
$authenticated_response = wp_remote_post( $url, array( 'body' => $authenticated_body ) );
次に、wp.getUsers
というメソッドの呼び出しをし、ログインを試します。
ここでようやく xmlrpc_enabled
を通るので無効化されていると判定されてくれれば良いのですが。。
デモのメソッドを呼び出す
WordPressのXML-RPCにはデモ用のメソッドが2つ用意されています。
'demo.sayHello' => 'this:sayHello',
'demo.addTwoNumbers' => 'this:addTwoNumbers',
WPScanでは、最後にデモ用のメソッドを呼び出して確認しています。
// Try an unauthenticated request.
$unauthenticated_body = '<?xml version="1.0" encoding="iso-8859-1"?><methodCall><methodName>demo.sayHello</methodName><params><param></param></params></methodCall>';
$unauthenticated_response = wp_remote_post( $url, array( 'body' => $unauthenticated_body ) );
if ( preg_match( '/<string>Hello!<\/string>/', $unauthenticated_response['body'] ) ) {
$this->add_vulnerability( __( 'The XML-RPC interface is partly disabled, but still allows unauthenticated requests.', 'wpscan' ), 'low', sanitize_title( $url ) );
}
デモ用のメソッドはXML-RPCが有効か無効かに関わらず、レスポンスが返ってきます。
このままだとWPScanはXML-RPCが一部有効と判定してしまって許してくれないようです。。
XML-RPCを無効化する本来の目的はブルートフォースアタックなどの攻撃を回避するためであって、WPScanに許してもらうわけではないので、hookでやるにしてもxmlrpc_enabled
だけで足りているとは思うのですが、なんだか気持ち悪いので解消方法を考えてみました。
デモのメソッドを削除する
デモのメソッドは wp_xmlrpc_server
クラスの $methods
に格納されています。
これを上書きするxmlrpc_methods
というfilterがあるのでそれを利用します。
add_filter( 'xmlrpc_methods', 'remove_demo_method', 10 );
public function remove_demo_method( $methods ) {
unset( $methods['demo.sayHello'] );
return $methods;
}
再度、リクエストを投げてみます。
該当のメソッドが存在しないことが確認できました。