WordPress Core ソースを読んでみよう (投稿更新編)

この記事は WordPress Advent Calendar 2014 の 10日めの記事として書いています。

WordPress に限らず Advent Calendar への参加は初めてです。 11月の終わりになってそう言えばそんな時期だなと思い、今年はどんなカレンダーがあるのだろうと Web サーフィン (死語) をしていたところ、WordPress Advent Calendar が埋まっていないのを見つけました。 そして、枠を埋めるつもりで気軽に応募し、この記事を書いています。

ちなみに私自身は WordPress プラグインの作者 (プラグインその1その2) ですが、本業は WordPress に関係ありません。 Advent Calendar 用として何を書くかについてはいろいろ悩みました。 利用者向けに書いてみたい気もしましたが、説教臭くなってしまう恐れがあるので(「何故、自前の WordPress なんでしょうか?ブログサービスじゃダメなんでしょうか?」とかw)、結局開発に興味ある方向けに、解説されることの少ない投稿の更新処理についてコードを追いかけてみることにしました。

というわけで今回は投稿編集画面で「公開」/「更新」ボタンを押したときの処理を見ていきます。 是非実際にコードを参照しながらお読みください。

なお、コードは WordPress 4.01 のものを参照しています。 紛らわしいのですが、HTTP メソッドの POST は大文字で、WordPress の投稿を指す際は小文字の post で表すことにします。

フォームの submit

まず、投稿編集画面ですが、wp-admin/post-new.php (新規) あるいは wp-admin/post.php (更新) によって表示されます。 編集画面を表示する実体としてはどちらも wp-admin/edit-form-advanced.php によって処理されるのですが、ここには深入りしません。

post-new.php の処理で注目したいのが、71行目で get_default_post_to_edit() を呼んでいる点です。 この時点でデータベース内に投稿データが作成され、新しい post ID が付与されることになります。 つまり、新規投稿であろうが更新であろうが、公開/更新ボタンを押したときの処理はデータベース内の post データを更新する処理になり、実のところ submit されたフォームデータの処理から先は共通になっています。

編集画面は post.php に POST するフォーム (form 要素は id=post) になっています。 hidden 項目の一つとして (name=’action’, value=’editpost’) という項目が POST されます。 これが wp-admin/post.php 17行目の wp_reset_vars() で $action = ‘editpost’ となり、104行目からの switch ($action) で edit_post() が実行されます。

以下関数名を使って追いかけていきます。

edit_post() in wp-admin/include/post.php

edit_post() では、引数が null で実行されることになるので POST されたデータが $post_data に設定されます。 $post_ID も POST された値が用いられます。

そしてもろもろの前処理 (詳細省略) を行った後 320行目で wp_update_post() を呼びます。

wp_update_post() in wp-includes/post.php

引数の $postarr には編集画面のフォームより POST された投稿データが含まれています。 3541行目の get_post() で $post にデータベース内の投稿データがセットされます。 attachment の更新の場合は wp_insert_attachment() をコールしたりとかありますが、今回は post なので wp_insert_post() が呼ばれます。 まあ、大した処理をしていないのでさっさと次に行きましょう。

次の wp_insert_post() の処理が最も重要です。

wp_insert_post () in wp-includes/post.php

引数 $postarr には編集画面のフォームより入力された投稿データが含まれています。 wp_update_post() に続きここでも get_post() でデータベースの投稿データを引っ張りだしています (3095行目)。 非効率な気もしますが、get_post() から呼ばれる get_instance() で読んだデータをキャッシュする仕組みがあるようなので大丈夫でしょう。

3090行目からの if 文の処理ですが、編集画面フォームから submit したときの処理は新規の場合でも $postarr[‘ID’] が存在して $update = true になります。 では $update = false となるのはどんな時かと言うと、編集画面を表示する際に get_default_post_to_edit() 経由で呼ばれたときですね。

そして、この wp_insert_post() はかなり長いのですが、やっていることの大半は post データの設定です。 どこでデータベースが更新されるかわかりづらいのですが、$update = true の場合は 3341行目の $wpdb->update() で更新されます。 この周辺にはアクションフック/フィルターフックも多数存在するので、この wp_update_post() だけでも一度真剣に読んでみると良いと思います。

さて、ここまでで投稿されたデータは無事データベースに反映されましたが、リビジョンの処理についてもう少し見てみます。

wp-includes/default-filters.php 内で既定のフック関数が登録されているのですが、この中に post_updated のフック関数として wp_save_post_revision() が登録されています (251行目)。 これが wp_insert_post() から呼び出されます (3478行目)。

wp_save_post_revision() in wp-includes/revision.php

それではリビジョン関連処理を見ていきます。 下の図は投稿更新時のリビジョン関連処理の概要を表したものです。 同じ色は同じコンテンツを表していますが、このように現在の post の内容と同一のものが最新リビジョンとして post_type=’revision’ でデータベース内に存在します。 詳しくはこちらの記事をどうぞ。

Update flow 3.6

では wp_save_post_revision() の処理を見ていきます。

まず wp_get_post_revisions() で更新された投稿についての全リビジョンを取得します (104行目)。 そして、最新のリビジョン、すなわち更新前の post と同じデータを持つリビジョンの ID を $last_revision にセットします (106行目からの foreach ループ)。 続く、127行目の if 文では内容に変更があったかを確認し、変更が無いようであればそのまま return してしまいます。

その後に _wp_put_post_revision() を呼んで新しい post と同じ内容を持つ post_type=’revision’ のデータをデータベース内に作成します (142行目)。 この処理において _wp_put_post_revision() から wp_insert_post() が呼ばれるわけです。 「$postarr のデータが post_type=’post’ でなく、post_type=’revision’ だったら処理はどう変わるのだろう?」という新たな視点から wp_insert_post() を読み直すことができるわけですねw

wp_put_post_revision() を呼んだ後 (144行目以降) は不要となったリビジョンの削除処理です。 保持するリビジョン数は wp_revisions_to_keep() が返す値を使っていますが、この関数の中をみると、WP_POST_REVISIONS を設定する他にも wp_revisions_to_keep フックをつくれば何世代リビジョンを保持するかを指定できることがわかります。

まとめ

ここまで説明してきた関数をコールツリーにまとめておきます。

call-tree

いかがでしょう? 思ったより簡単に読めたのではないでしょうか?

この記事がきっかけで Core のコードに興味を持ち、実際にプラグインを書いたりする人が増えるととても嬉しいです。 他にも WordPress 関連記事はありますので、気が向いたらどうぞ。 なお、投稿更新「編」としましたが、関連記事がこれっきりになる可能性がそれなりに高いことをあらかじめご承知おきくださいw

明日は岡本秀高さん濱田文さんです。 何と 2つの記事が読めちゃいます!

スパム 0、アタック対象 ID とパスワード

何のことかと言うと、WordPress のセキュリティ関連のお話です。

スパムをシャットアウト

画像で示した通り、9月終わりに SI CAPTCHA プラグインに手を入れてからコメントスパムをシャットアウトしています。 やったね!

一応、公式フォーラムに「文字数と画像サイズを可変にして欲しい!」って書いておきましたが、何の音沙汰もないので対応は望み薄ですね。 対策したい人は先の記事を参考にしてみてください。

wp-login.php へのアタック

それと、同じ頃に WordPress ログイン画面に入力された ID とパスワードを記録するようにしました。 テーブルに情報が溜まるようにしたのですが、約 1ヶ月でトータル 11万件ほどになってますw 今日はアタックに使われている ID、パスワードの Top 20 を大公開しちゃいます。

> select login, count(*)
> from login_table
> group by login
> order by count(*) desc
> limit 20;
+--------------------+----------+
| login              | count(*) |
+--------------------+----------+
| admin              |    72358 |
| administrator      |     9277 |
| user               |     4704 |
| user2              |     4680 |
| adm                |     4675 |
| tester             |     4672 |
| test               |     3872 |
| support            |     3513 |
| en                 |     2757 |
| en.hetarena.com    |     2752 |
| author             |     2186 |
| blogger323         |     1473 |
|                    |      373 |
| hetarena           |      222 |
| root               |      195 |
| en@en.hetarena.com |       60 |
| demo               |       50 |
| developer          |       46 |
| webmaster          |       45 |
| admin1             |       44 |
+--------------------+----------+
20 rows in set (0.09 sec)

これを見ると admin が危ないのはもちろんですが、著者名 (blogger323) もアタックに使われてますね。 正しくログイン名を守る方法はこちらの記事を参照してください。 ちなみに en.hetarena.com というのは英語版のホスト名です。

続いてパスワード Top 20。

> select password, count(*)
> from login_table
> group by password
> order by count(*) desc
> limit 20;
+---------------+----------+
| password      | count(*) |
+---------------+----------+
| admin         |      573 |
| 123456        |      174 |
| password      |      159 |
| admin123      |      157 |
| 123123        |      146 |
| qwerty        |      145 |
| pass          |      144 |
| 111111        |      137 |
| 12345678      |      127 |
| abc123        |      125 |
| 123456789     |      123 |
| 12345         |      122 |
| 1234567       |      121 |
| 1234          |      112 |
| 123321        |      111 |
| administrator |      109 |
| password1     |      108 |
| root          |      108 |
| admin1        |      100 |
| INTERNET      |      100 |
+---------------+----------+
20 rows in set (0.15 sec)

簡単なパスワードはダメですよと。 パスワードの方は下の 20 も見てみましょうかね。 (件数 1件のものはもっとありますが、SQL 文が返した 20件ということで)

+--------------+----------+
| password     | count(*) |
+--------------+----------+
| destiny1     |        1 |
| ##           |        1 |
| gabber       |        1 |
| milad        |        1 |
| 1?2?3?4?     |        1 |
| ra           |        1 |
| archibold    |        1 |
| cirstoforo   |        1 |
| blondy       |        1 |
| deadgirl     |        1 |
| lookup       |        1 |
| react        |        1 |
| prima        |        1 |
| padraig      |        1 |
| sebastiano   |        1 |
| webmasterddd |        1 |
| Tuulia       |        1 |
| abigale      |        1 |
| yasicheng    |        1 |
| qwe1234      |        1 |
+--------------+----------+

何だかよくわかりませんね。 ただ、一つ言えるのは最後の例のようにキーボード配列を使ったパターンはシフトキーの組み合わせ含め様々なものが試されていたので、避けるのが賢明です。

ディスク I/O 100% 状態からの復旧

Windows 8.1 を使っていて、ディスク I/O が 100% に張り付いて使い物にならないという状況に苦しめられました。 最終的にはその状態から回復できたので、私が行ったことをメモ書きしておきます。

きっかけ

小学生の子供達にもその Windows 8 マシンを使わせるため、ペアレンタルコントロールの設定をしました。 Windows 8 では 1日の使用時間の総量を制限することができるのですが、制限時間到達後に電源長押しを行って強制再起動すると何故かまた使えるようになるのだそうです (子供達の経験談)。 それを何度も何度も繰り返していたおかげで、一部のファイルの整合性が損なわれたようです。

ひどい子供達だ…。

状況は?

タスクマネージャーの「パフォーマンス」タブで確認するとディスクの「アクティブな時間」がずっと 100% に張り付いてしまって、何をやるにしても反応が遅く使い物になりませんでした。 Windows Update サービスを無効化すると症状が改善されたりしたので、当初は Windows Update 関連かと思ったのですが、イベントマネージャーの Windows ログ – Application を確認するとソースが ESENT のエラーが多数見受けられました。

初めて知ったのですが、ESENT とは拡張ストレージエンジン (Extensible Storage Engine) だそうで、Windows Update や Windows デスクトップ サーチで利用されているそうです。 で、子供たちの強制電源オフであちらこちらに論理障害が起こっていたと。 そういうことのようです。

何をやったか?

以下のあたりのファイルを消しました。 Windows Update 関連とイベントビューアーのログに書かれていたものです。

C:\Windows\SoftwareDistribution\DataStore
C:\Windows\SoftwareDistribution\Download
C:\ProgramData\Microsoft\Windows\AppRepository\*.log
C:\Users\ユーザー名\AppData\Local\Microsoft\Windows\WebCache

アプリケーションが使用中でロックされているものが一部あったりもしましたが、消せるものを消したら何とかなったようです。

それと、ESENT と関係があるかどうかわかりませんが、Windows Store 関連も怪しい動きだったので、以下のブログの記事の内容を実行しています。

具体的には以下の 2つのコマンドを実行しています。

powershell -ExecutionPolicy Unrestricted Add-AppxPackage -DisableDevelopmentMode -Register $Env:SystemRoot\WinStore\AppxManifest.xml

これでやっと使えるようになりました。 子供には間接的にも直接的にもいろんな形で時間を取られちゃいますね。 今だけなんでしょうが。

MIDI コントローラーを使いこなせ!

今回は Cubase のクイックコントロール機能の紹介です。 以前も MIDI コントローラーに関するエントリを書きましたが、「一般リモートデバイス」として UC-33e という MIDI サーフェスを登録する方法についての説明でした。 しかし、最近 (個人的な感覚ですw) の Cubase ではクイックコントロールという機能が装備され、VST シンセの音色いじり等の用途では最大 8つのパラメータをコントロールすることができるこのクイックコントロール機能で十分です。 お手軽に使えるので使ってみましょう。

デバイスの登録

まず、デバイス設定画面でコントローラー使うための設定を行います。 「デバイス」-「デバイス設定」で画面を開き左のツリーより「トラッククイックコントロール」を選択します。

まずはじめに、USB などで PC に直接接続しているコントローラーであれば「MIDI 入力」は「All MIDI Inputs」ではなく個々のコントローラーを選んでおきましょう。

続いて個々のコントロール (ツマミ、フェーダー等) のアサインです。 「学習」をチェックした後、割り当てたいコントロール (QuickControl1 ~ 8 のどれか) を選択してからコントローラーのツマミを操作すると送られた信号より MIDI コントロールチェンジ番号を認識してくれます。 登録したいコントロールの数だけ設定を行います。

Quick Control

Cubase 7.5 より「トラックコントロール」の他に「VST クイックコントロール」も存在しています。 VST クイックコントロールの使い方については後ほど説明します。 ツマミが少ない人はトラッククイックコントロールの方だけ設定しておけばよいと思いますが、余裕があれば VST クイックコントロールの方にも設定しておきましょう。 私の例だと、UC-33e の 8本のスライダーをトラッククイックコントロール、8個のツマミを VST クイックコントロールに割り当てて使っていたりします。

コントローラーを「All MIDI Inputs」から外す

クイックコントロール用として指定したコントローラーからの信号が余計なところに流れて副作用を起こさないように「All MIDI Inputs」から外しておきます。 これはデバイス設定の「MIDI ポートの設定」で行います。

Quick Control

トラッククイックコントロールの操作

トラッククイックコントロールで設定した MIDI コントローラーを使って選択されているトラックに信号を送ることができます。 どのパラメーターが操作されるかは、インスペクターのクイックコントロールタブで指定することができます。

Quick Control

クイックコントロールの設定はトラックプリセットとして保存できる他、上図のようにクイックコントロールのみのプリセットとしても保存できます。

VST クイックコントロールの操作

Cubase 7.5 より VST インストゥルメントの画面 (ラック画面) が変更になっているのですが、VST クイックコントロールを使ってこのラック内で選択した VST インストゥルメントを操作できます。 選択されているインストゥルメントは右下のアイコンが橙になっています。 このアイコンを直接クリックしたり、ヘッダ部分の上下矢印アイコンを使うことで選択を変更することができます。

Quick Control

コントローラーの選び方

最近はツマミがたくさんついていて DAW ソフトウェアとの親和性も高い MIDI キーボードがあるのでそれで十分な気がしますが、もし私が新たに独立したコントローラーを買うのであれば以下の点を気にします。

  • 演奏時にシンセの音色をいじれるだけの数のツマミ (4つぐらいでも十分な気が…)
  • 省スペース
  • トランスポート (録音、再生、etc) 操作は PC キーボードのショートカットで行えば十分なので不要。 Cubase iC Pro のような選択肢もありますしね。

ということを考えると、まさに純正クイックコントロール用 CMC-QC なんて良いのではないでしょうか。 Native Instruments が最近出したキーボードも気にはなります。

まあ、私の場合は今使っているものが壊れなければ買うことはないでしょうが。

更なる情報

興味がある方は公式ビデオが存在するので、是非見ておいてください。 インストゥルメントトラックの拡張についても説明されています。

未だに日本語の字幕がついていないようなので、こちらの記事もどうぞ。

Angular.js のチュートリアルを Windows で実行するために

Angular.js 公式チュートリアルの「Working with the code」(環境構築から最初のテスト実行まで) を Windows 環境で実行するための補足です。 あくまでも補足ですので、手順自体は公式サイトを参照してください。 Windows Server 2012 と Windows 7 で試しました。

関連ツールのインストール

まず、必要なものから。

  • Node.js をインストールする。インストーラ (.msi) をダウンロードして実行でオッケー。
  • Git for Windows をインストールしておく。インストールの際に ‘Use Git from the Windows Command Prompt’ を指定するか、コントロールパネルから Git の bin ディレクトリ (デフォルトでは \Program Files (x86)\Git\bin) にパスを通しておく。
  • Chrome をインストールしておく。
  • Java 実行環境をインストールしておく。

その後は「Node.js command prompt」を起動して説明通りにコマンドを入力しますが、git や java へのパスが通っていないと失敗するので気をつけましょう。

Optional となっていますが、以下も実行しておきましょう。

npm install -g bower

テストの実行

テストの仕方ですが、チュートリアルで説明されている通り、

npm start

を実行すると 8000/TCP でテスト用サーバーが動きます。 そこで別の「Node.js command prompt」を起動し、テスト用のコマンドを実行します。

Karma による Unit Test では Chrome が起動して「connected」と表示され、「何じゃそりゃ?」という感じなのですが、コマンドプロンプトにテスト結果が SUCCESS と表示されていればオッケーです。 テスト後は Chrome を終了しても再起動してしまうので、コマンドプロンプトの方に Ctrl+C を送ります。

きちんと説明を読むと変更を検知してテストし直すので Karma は起動しっぱなしにするのがお勧めと書いてあったりします。

Protractor による End to End Test はテストしている感があって面白いですね。

英語の苦手な人も頑張ってみましょう。

Firewalld の設定

CentOS 7 への移行に伴い iptables から Firewalld へ移行したのですが、Firewalld の wiki を見てもよくわからなかったところの覚書。

interface と source は OR 条件

サーバーに接続できる IP アドレスを制限しようとして、例えば以下のように設定したとします。

# firewall-cmd --zone=home --add-interface=eth0
# firewall-cmd --zone=home --add-source=11.22.33.44/32

すると意図に反して eth0 を使った通信は全て許可されてしまいます。 interface が eth0、またはソースアドレスが 11.22.33.44 の通信が許可されるのです。

正しくは –add-source のみ実施します。

現行設定と起動時設定は別々に行う

設定をすぐに反映させ、さらに起動時にもその設定を残るようにするには以下の 2つのコマンドを入力しなければなりません。

# firewall-cmd --zone=home --add-source=11.22.33.44/32
# firewall-cmd --permanent --zone=home --add-source=11.22.33.44/32

最初の行で現在の設定が変更され、–permanent をつけることで永続的な設定、すなわち firewalld 起動時の設定が変更されます。

まあ、移行してしまえば、全体的には iptables よりわかりやすいのかも知れませんが。

WordPress のユーザー名と表示名

プラグインの機能拡張の時に WordPress のユーザー名関連について調べたのでまとめておきます。

ユーザー名に関する以下の項目が WP_USERS テーブルに入っています。

user_login
ログイン時に使う名前
display_name
表示用の名前
user_nicename
著者別の URL 用 (例: https://hetarena.com/archives/author/blogger323) に使われる名前

重要なのがログイン画面で入力する user_login です。 サイトへのアタックに使われると嫌なのでこれは知られたくないですね。 そこで表示用に display_name (管理画面上は「ブログ用の表示名」) というものがあって、これを設定しておくと投稿の表示などにはこちらが使われます。

ただ、ちょっと曲者なのが、user_nicename です。 これは user_login を元に自動的に設定され、管理画面から変えることができません。 そして、リダイレクトのテクニックを使うと訪問者にわかってしまいます。

どういうことかというとサイトの設定によっては以下の形式の URL にアクセスすると user_nicename を使った著者別 URL にリダイレクトされるのです。

https://hetarena.com/?author=1

このサイトでは author=1 だと URL には admin が表示されます。 admin と表示されるのは嫌だと思っても管理用 GUI からこれを変えることができません。 この動作があるので「ブログ用の表示名」を変えただけでは不十分であり、皆さんログイン名情報を隠すのに苦労しているようです。

そんな悩みをお持ちの方にちょっとしたテクニックをお伝えしておきます。 私の場合は逆にユーザー作成後に user_login を SQL 文で変えてしまっています。

UPDATE wp_users
SET user_login = 'himitsu'
WHERE id = 1;

ユーザー作成時のアカウント名はそのまま表示用として display_name と user_nicename に残しておけば良いわけです。 SQL 文でやらねばならないところがネックかも知れませんが…。

ちなみに user_nicename のデフォルト値ですが、WordPress Core の user.php 内 wp_insert_user() で以下のようにセットされてます。

$user_nicename = sanitize_title( $user_login );

それと「ニックネーム」は WP_USERS テーブルではなくて WP_USERMETA の方に保管されます。

なお、そもそもの話としては複雑なパスワードを使うというのが一番重要であることを最後に強調しておきたいと思います。

WordPress でのコメントスパムとの戦い

このブログのように弱小サイトでもコメントスパムが来ます。 最盛期は 1か月に 6万7千件という記録があります。 最近は減ってきたものの、それでも管理画面にログインして「200件のコメントがスパムとして捕えられています」というようなメッセージを見るのは気分がいいものではありません。 今回、原因調査と対策をしたことでスパムキューに入るコメントの数そのものを減らすことができたので、経緯をまとめておきたいと思います。

スパムキューに入るコメントの数を減らしたい

使っている人は誰でも知っていると思いますが、WordPress には標準で Akismet というスパム対策用のプラグインがついてきます。 個人用には無償で利用でき、これはこれで有難いプラグインなのですが、日本語についてはたまに誤検知が発生するようです。 少なくとも私は正規のコメントをスパムキューに入れられた経験が複数回あります。

そういうことがあると、どうしてもスパムキューのチェックをしたくなってしまいます。 スパムと正規コメントがそれこそ 10,000 : 1 ぐらいの比で入っているようなキューをチェックするのは至難の業です。 やはり、根本的な対策を行ってスパムキューに入るコメントの数を減らしたい。

そこで私は CAPTCHA プラグインを導入してみました。

SI CAPCHA を導入したのにスパムが減らない?

私が導入したのは SI CAPTCHA プラグインです。 レビュー評価も高く、最近もメンテナンスされている様子なのでこれを選びました。

しかし、何故か導入後も思ったようにスパムキューに入るコメントの数が減らなかったのです。 そこで、この原因を調べてみました。

人間じゃないな

まずは httpd のログを確認します。 スパムコメントが投稿される際のログはたった 4行でした。

192.168.138.148 - - [27/Sep/2014:10:28:03 +0900] "GET /archives/503 HTTP/1.0" 200 60116
192.168.138.148 - - [27/Sep/2014:10:28:05 +0900] "GET /wp-content/plugins/si-captcha-for-wordpress/captcha/securimage_show.php?si_form_id=com&prefix=TdkHIF8aBTtW9BWL HTTP/1.0" 200 6099
192.168.138.148 - - [27/Sep/2014:10:28:05 +0900] "POST /wp-comments-post.php HTTP/1.0" 302 -
192.168.138.148 - - [27/Sep/2014:10:28:09 +0900] "GET /archives/503 HTTP/1.0" 200 60260

通常の Web ブラウザを使った閲覧ならば、1行目のページ全体の html がダウンロードされた後に関連する画像や JavaScript 等がダウンロードされるのですが、それは行われていません。 CAPTCHA の画像 (2行目) をダウンロード後、コメントを POST (3行目) し、再度そのページを取得 (4行目) しているだけです。

注目すべきは 2行目と 3行目の間の経過時間、すなわち CAPTCHA 画像をダウンロードしてからコメントを投稿するまでの時間です。 これをみると 1秒も経たずに行われています。

以前、この世界には安価な人力を使って CAPTCHA を突破するような仕組みが存在するという話をどこかで見かけたのですが、このログを見る限り人間が処理しているわけではないようです。 私はこの時点は 2つの可能性を考えていました。 一つはプラグインが何か脆弱性を持っていてそこを突かれている可能性、もう一つは CAPCHA 画像を機械的に認識処理している可能性です。

作者の方には申し訳ないのですが、ぼんやりと脆弱性の可能性の方が高いように考えていました。

SI CAPTCHA の仕組み

SI CAPTCHA の仕組みを簡単に説明しておきます。

ブログのページが表示されたとき、4文字の CAPTCHA 文字列 (captcha_code) がランダムに生成され、その内容は cache ディレクトリ内の一時ファイルに保存されます。 保存されたファイル名にはランダムの文字列 (si_code_com) が付与されます。 先のログの「TdkHIF8aBTtW9BWL」の部分がこの si_code_com に相当します。

CAPTCHA

コメント投稿時に POST された captcha_code と si_code_com から一時ファイルの内容と合っているかを確認し、合っていなければエラーとする仕組みです。 上の図はこれらの処理をイメージ化したものです。 この例では si_code_com = TdkHIF8aBTtW9BWL、captcha_code = 9YgA です。

画像認識処理されてるよ!

SI CAPTCHA のソースコードを読んでこれらを把握したのですが、コードを読むだけでは特に問題となりそうな箇所は見つけることができませんでした。 そこで、投稿されている内容をログに記録することにしました。 具体的には si-captcha.php 内にある関数 si_captcha_comment_post() で $_POST の値を確認するようにし、プログラムが生成時に使用している captcha_code や si_code_com と比べてみました。

すると驚いたことにスパマーは正しい captcha_code と si_code_com を送ってきていたのです。 一応付け加えておくと出鱈目な captcha_code を付けて POST してくるケースもあったのですが、それらはきちんと弾かれていました。

captcha_code と si_code_com が正しいとなるとどうやら画像認識処理をしているようです。 作者さん、疑ってごめんなさい!

キューに入る前にシャットアウト!

さて、画像認識処理が機械的に行われているとなるとどうすればよいでしょうか。 高度な画像認識処理が行われていて、認識率が結構高いとなるとやっかいです。 プラグインを変えなければならないかも知れません。

それは面倒だなあと思いつつ、まずはできるところを試してみようと思いました。 何を行ったかというと SI CAPTCHA のコードをいじって文字数と画像サイズを変えたのです。

すると、スパマーが投稿する際の captcha_code に変化が見られました。

["captcha_code"]=> string(14) "!UNKNOWN_TYPE!"

やったね!!

これだけで Unknown type とか言っちゃう程度の処理エンジンであればこの対策だけで十分な気がしてきました。 恐らく画像認識処理は SI CAPTCHA の文字数と画像サイズに最適化されていたのでしょう。

これでキューに入る前に弾かれるようになりました。

変更箇所

修正の仕方が広まってスパマー側に対策されるのが嫌なので、私がどのように変えたかはあえて記しません。 興味があれば、実際のコメント欄をご覧ください。(丸わかりですね)

ここでは変更箇所だけお伝えしておきます。 それだけじゃコードを修正できないという方は、是非 SI CAPTCHA プラグインのサポートページで文字数や画像サイズが可変となるよう要望を出してみてください。

  • si-captcha.php の関数 si_captcha_captcha_html() 内の以下部分の width を変更

      // url for no session captcha image
      $securimage_show_url = $si_captcha_url .'/securimage_show.php?';
      $securimage_size = 'width="175" height="60"';
      if($si_captcha_opt['si_captcha_captcha_small'] == 'true' || $label == 'si_image_side_login' ) {
        $securimage_show_url .= 'si_sm_captcha=1&';
        $securimage_size = 'width="132" height="45"';
      }
    

    これらは管理画面の CSS で変えられるかも知れません。

  • secureimage_show.php の $char_length と $img->image_width の値

宣伝!

話が全然違いますが、私もプラグイン作者なのです。 以下のプラグインも是非使ってみてください!

PhpStrom 8 で WordPress プラグイン開発が快適に!

9月中旬に PhpStorm 8 がリリースされたので、ちょっと間が空きましたがバージョンアップして触ってみました。 このバージョンから WordPress に関するサポート機能が追加されています。

WordPress 開発環境のサポート

PhpStorm 8 の WordPress 関連機能の詳細はこちらのページで確認できますし、こちらのブログで丁寧に紹介されているので、ここではできることを簡単に箇条書きでまとめるにとどめておきます。

WordPress 用 Code Style
WordPress 用 Code Style が用意され、WordPress のコーディング基準に従ってフォーマットされます。 既存のコードに適用するには「Reformat Code」ですね。
フック (filter/action) 名称のの入力補完
WordPress Core で宣言されているフック名を入力補完してくれます。
フックの呼び出し箇所へのジャンプ
例えば、

add_action( 'admin_init', 'my_hook' );

というコードを書いているときに行頭カラム内のアイコンをクリックすると、

do_action( 'admin_init' );

というようなフックの呼び出し場所へ一発で飛ぶことができます。

フック関数へのジャンプ
上の例で add_action の例で ‘my_hook’ の部分を Ctrl + マウスクリック をすることで ‘my_hook’ の宣言箇所へジャンプすることができます。 クラスメンバ関数をフック関数として登録している場合もちゃんと飛んでくれました!
WordPress.org 内の検索
関数名などの文字列を選択した後のコンテキストメニューより「Search on WordPress.org」を実行できるようになりました。 なお、検索結果は新しい開発者ポータルである developer.wordpress.org のページが表示されます。 (developer.wordpress.org に関する情報はこちら)
WP-CLI の統合
WP-CLI を Command Line Tool Console から使うことができます。 各種オプションの補完も効きます。 WP-CLI そのものについては公式サイト「サイトの拡張性を飛躍的に高める WordPressプラグイン開発のバイブル」をどうぞ。

ついでに紹介 – JetBrains Open Source License

PhpStorm の開発元 JetBrains からオープンソース開発者向けに Open Source License という無償ライセンス提供プログラムが提供されています。 開発プロジェクトの情報 (公式ホームページ、開発レポジトリやフォーラムの URL 等) を送り審査を受けて基準が満たされていればライセンスが発行されます。 英語でのやりとりとなりますが、WordPress.org に自作品を登録しているプラグイン開発者であれば英語にも慣れているでしょうし、入力項目としても WordPress.org のレポジトリやサポートフォーラムを伝えればよいのでそれほどハードルは高くないと思います。 興味のある方はどうぞ。

WordPress リビジョンメモ機能強化!

Thin Out Revisions 1.7 をリリースしました。このバージョンではリビジョンメモ機能が強化され、投稿にリビジョンメモを表示できるようになりました。 余裕がなくて日本語リソースができていないのですが、その分この記事で説明しておきます。

以下のようなパラメータが TOR の設定画面に追加されています。 「On」を選ぶとこの機能が有効になります。 また、デフォルトの状態を「Show」(表示) とするか「Hide」(隠す) とするかを選択できます。

TOR 設定画面
TOR 設定画面

個別の投稿設定で「Show」、「Hide」を選ぶこともできます。 こちらの設定が優先されます。

投稿の編集画面
投稿の編集画面

メモを表示する場合もその投稿の一部のメモを隠すことができます。 先頭に「#」(半角) をつけてください。

投稿の編集画面
投稿の編集画面

上のメモがあるときに投稿上のメモ表示は下の通りになります。

投稿に表示されたメモ
投稿に表示されたメモ

メモ自体は DL としてマークアップされます。 DL の前の見出しは設定画面で入力できますが、いくつかのタグ (<script> 等) は使えません。 入れても削除されます。

リビジョンメモ機能もリビジョン削除機能に劣らず便利なので、是非使ってみてください。 まだ更新できていない日本語公式ページはこちら (苦笑)。