ふつーにPingback飛んできてた。こういうことがあるからWebサーバレベルで塞いでたんだけど、JetpackプラグインでWordpress.comと接続するのにXMLRPCを使っているらしく制限を取っ払ってた。他にも設定してたので大丈夫だろうという判断だったんけど大甘だった。
何を信用していいかわからなかったのでコードを読んで、実際に実行して確認した。そもそも前回なぜ俺は動作確認をちゃんとしなかったのか。
なお、これから書くのは全て WordPress 3.9.1 における情報となる。
xmlrpc_enabled
フィルタでPinbackは防げない
add_filter('xmlrpc_enabled', '__return_false');
これで防げると思ってたんだけど 実際 pingback.ping
は呼び出せた。WordPressのソースを確認したら xmlrpc_enabled
フィルタでチェックしているのは認証が必要なパターンのときだった。pingback.ping
は防げない・・・
というわけでPinbackを無効にするにはやっぱりxmlrpc_methods
フィルタを使うのが良い。
if ( function_exists( 'add_filter' ) ) {
add_filter( 'xmlrpc_methods', 'remove_xmlrpc_pingback_ping' );
}
function remove_xmlrpc_pingback_ping($methods)
{
unset($methods['pingback.ping']);
return $methods;
}
しかしながら xmlrpc_enabled
の方が包括してくれると誤解を招くトラップ。名前が悪いのか、動作が悪いのか。
デフォルトの投稿設定は過去の投稿には影響しない
投稿のデフォルト設定
の 他のブログからの通知 (ピンバック・トラックバック) を受け付ける
のチェックを外していたんだけど、この設定はこれから作成する投稿のデフォルト設定なので、 過去の投稿の設定には影響しない。
WordPressは投稿ごとにコメントやピングバックを受け付けるかを設定できる。投稿のデフォルト設定
を変えても過去の投稿のその設定は変わらないのである。過去の投稿はまったくの無防備だった。
wp-config.php
でもフィルタが動く
最初Pingbackが飛んできて疑ったのは、そもそも「XMLRPCの呼び出し時にテーマのfunction.phpは参照されるのか」と。しかし、実際XMLRPCを呼び出して確認したところ呼ばれていた。
テーマで制御したくなかったのはテーマを変えると設定が失われてしまうからである。そこでwp-config.php
にフィルタの処理を入れてみたがこれが動作した。というわけでxmlrpc_methods
フィルタの指定はwp-config.php
のファイルの最後に入れることにした。
/** Sets up WordPress vars and included files. */
require_once(ABSPATH . 'wp-settings.php');
add_filter('xmlrpc_enabled', '__return_false');
add_filter('xmlrpc_methods', 'remove_xmlrpc_pingback_ping');
function remove_xmlrpc_pingback_ping($methods)
{
unset($methods['pingback.ping']);
return $methods;
}
やっぱりWebサーバで塞ぐ
と、ここまで調べたけどWordPressのバージョンが変わって動作が変わることもありえるので、やっぱりWebサーバで塞ぐのが確実。面倒だけど必要な時だけNginxの以下の設定をコメントアウトしてxmlrpc.php
へのアクセスを有効にすることに。
# disable XML-RPC
location = /xmlrpc.php {
deny all;
}
踏み台攻撃怖い
http://codex.wordpress.org/XML-RPC_Pingback_API
string sourceUri
string targetUri
Pingback_APIの動作確認していてわかったけど sourceUri
のページの中身にtargetUri
が含まれていないとエラーになる。つまり相手先がちゃんとこちらのブログのページのUrlをリンクしていないとそのPingbackは無効になる。この場合スパムとしても残らない。
また踏み台にされた場合にsourceUri
の中身をの取りに行く(これが攻撃になる)がこれはクロールに等しくリソースの取得やJavascriptの実行は行われないのでGoogle Analyticsなどのアクセス数に加算されない。
つまりサーバの監視をしていなければお互い踏み台にされていることも攻撃されていることに気がつかないわけである。しかし普通のブロガーにサーバの監視まで要求するのは難しい。
あとsourceUri
の中身をの取りに行くまでにtargetUri
のhostがget_option('home')
と一致しているか、targetUri
の投稿がPingbackを受け付ける設定なっているかが検証されているのでここでエラーになれば踏み台にならなくて済むけど、単純にこっちがDOS攻撃されるだけにもなる。
おまけ
仮想環境に環境構築してデータベースをコピーして、ひたすらXMLRPCを呼び出してた。
XMLRPCのメソッド一覧の取得。
curl localhost/xmlrpc.php -d '<methodCall>
<methodName>system.listMethods</methodName>
<params></params>
</methodCall>'
結果。これ見るとウェブサーバで塞ぎたくなるわ。
<?xml version="1.0" encoding="UTF-8"?>
<methodResponse>
<params>
<param>
<value>
<array><data>
<value><string>system.multicall</string></value>
<value><string>system.listMethods</string></value>
<value><string>system.getCapabilities</string></value>
<value><string>demo.addTwoNumbers</string></value>
<value><string>demo.sayHello</string></value>
<value><string>pingback.extensions.getPingbacks</string></value>
<value><string>pingback.ping</string></value>
<value><string>mt.publishPost</string></value>
<value><string>mt.getTrackbackPings</string></value>
<value><string>mt.supportedTextFilters</string></value>
<value><string>mt.supportedMethods</string></value>
<value><string>mt.setPostCategories</string></value>
<value><string>mt.getPostCategories</string></value>
<value><string>mt.getRecentPostTitles</string></value>
<value><string>mt.getCategoryList</string></value>
<value><string>metaWeblog.getUsersBlogs</string></value>
<value><string>metaWeblog.deletePost</string></value>
<value><string>metaWeblog.newMediaObject</string></value>
<value><string>metaWeblog.getCategories</string></value>
<value><string>metaWeblog.getRecentPosts</string></value>
<value><string>metaWeblog.getPost</string></value>
<value><string>metaWeblog.editPost</string></value>
<value><string>metaWeblog.newPost</string></value>
<value><string>blogger.deletePost</string></value>
<value><string>blogger.editPost</string></value>
<value><string>blogger.newPost</string></value>
<value><string>blogger.getRecentPosts</string></value>
<value><string>blogger.getPost</string></value>
<value><string>blogger.getUserInfo</string></value>
<value><string>blogger.getUsersBlogs</string></value>
<value><string>wp.restoreRevision</string></value>
<value><string>wp.getRevisions</string></value>
<value><string>wp.getPostTypes</string></value>
<value><string>wp.getPostType</string></value>
<value><string>wp.getPostFormats</string></value>
<value><string>wp.getMediaLibrary</string></value>
<value><string>wp.getMediaItem</string></value>
<value><string>wp.getCommentStatusList</string></value>
<value><string>wp.newComment</string></value>
<value><string>wp.editComment</string></value>
<value><string>wp.deleteComment</string></value>
<value><string>wp.getComments</string></value>
<value><string>wp.getComment</string></value>
<value><string>wp.setOptions</string></value>
<value><string>wp.getOptions</string></value>
<value><string>wp.getPageTemplates</string></value>
<value><string>wp.getPageStatusList</string></value>
<value><string>wp.getPostStatusList</string></value>
<value><string>wp.getCommentCount</string></value>
<value><string>wp.uploadFile</string></value>
<value><string>wp.suggestCategories</string></value>
<value><string>wp.deleteCategory</string></value>
<value><string>wp.newCategory</string></value>
<value><string>wp.getTags</string></value>
<value><string>wp.getCategories</string></value>
<value><string>wp.getAuthors</string></value>
<value><string>wp.getPageList</string></value>
<value><string>wp.editPage</string></value>
<value><string>wp.deletePage</string></value>
<value><string>wp.newPage</string></value>
<value><string>wp.getPages</string></value>
<value><string>wp.getPage</string></value>
<value><string>wp.editProfile</string></value>
<value><string>wp.getProfile</string></value>
<value><string>wp.getUsers</string></value>
<value><string>wp.getUser</string></value>
<value><string>wp.getTaxonomies</string></value>
<value><string>wp.getTaxonomy</string></value>
<value><string>wp.getTerms</string></value>
<value><string>wp.getTerm</string></value>
<value><string>wp.deleteTerm</string></value>
<value><string>wp.editTerm</string></value>
<value><string>wp.newTerm</string></value>
<value><string>wp.getPosts</string></value>
<value><string>wp.getPost</string></value>
<value><string>wp.deletePost</string></value>
<value><string>wp.editPost</string></value>
<value><string>wp.newPost</string></value>
<value><string>wp.getUsersBlogs</string></value>
</data></array>
</value>
</param>
</params>
</methodResponse>
Pingbackリクエスト。これを成功するには1番目のURLの内容に2番目のURLを含んでいて、2番目のURLの投稿が存在してPingbackを受け付ける設定になっていないといけない。結構ちゃんとテストするの面倒。
curl -D - localhost/xmlrpc.php -d '<methodCall>
<methodName>pingback.ping</methodName>
<params>
<param>
<value>
<string>http://example.com/</string>
</value>
</param>
<param>
<value>
<string>http://localhost/?p=1</string>
</value>
</param>
</params>
</methodCall>'
オレ、WordPressニ、RPCイラナイ。