WordPress のセキュリティ対策

河本孝之(Takayuki Kawamoto)

Contact: https://plus.google.com/112326853631114362590/about

First appeared: 2008-11-19 22:55:00;
Modified: , , , , , , ,
Last modified: .

WordPress のセキュリティ対策について取り上げます。2008年に公開したときは「前篇(基礎編)」「後篇」に分けていましたが、このたび再掲載するにあたり、一つのページにまとめました。コーディングレベルからプラグインの導入まで、ひととおり考え付く論点を挙げてみますので、追加したいポイントがあれば追加します。個人的な意見にもとづく対策プランも含まれているので、「試案」というよりも「私案」と呼ぶべきかもしれませんが、そのあたりは差し引いて、それぞれの事情に合わせて頂ければよいでしょう。

なお、本稿の末尾には、新しい情報と本稿の内容を比較したレビューを追加していきます。

最終アップデートにあたって (2015-10-07)

本稿では WordPress の「コアシステム」("barebone" と言われることもあります)については扱っていません。コアシステムとは、wordpress.org で公開・配布されている wordpress-4.3.1.zip のようなディストリビューション(データベースのパスワードなど、実装する環境に合わせた設定を調整するだけで使えるような状態で配布されているファイルのひとまとまり)のうち、wp-content フォルダ内のプラグイン(plugins フォルダの中身)やテーマ(themes フォルダの中身)を除外した、WordPress というウェブアプリケーションを動作させるための基本的なファイルの一式を指します。プラグインやテーマは、プロの開発者やデザイナーからプログラミングの素人まで、色々な人たちが(いわば勝手に)公開しています。もちろん、WordPress の運用に関わるリスクは、寧ろプラグインやテーマの方が高いわけですが、コアシステムの脆弱性について理解できる方であれば、プラグインやテーマの脆弱性について理解し対策するのは容易だと思います。WordPress のコアシステムについて脆弱性を検討している資料は少ないのですが、Check Point Software Technologies の脆弱性研究員であるルビン(Netanel Rubin)さんが書いた “Finding Vulnerabilities in Core WordPress: A Bug Hunter's Trilogy”(Part I on 2015-08-04, Part II on 2015-08-11, Part III on 2015-09-15)という連作の記事が参考になりますので、ご一読ください。

なお、ルビンさんの記事や幾つかの資料を基にして本稿の「番外編」を準備していたのですが、昨今は徳丸浩さんをはじめとして、まともなレベルの方々が WordPress にも積極的に言及され始めていますので、彼ら能力も責任もある人々が公開していくものを優先して紹介する方がよいと考え、ここでこれ以上 WordPress のセキュリティについて新しいドキュメントを作って公開するのは止めることとしました

簡単に言うと、ウェブコンテンツの reputation は「有名人が更に有名になる」ような仕組みが働くので、情報セキュリティに関わっている本職のドキュメントに多くの方の注意が向くことが自然です。アマチュア(僕は少なくともナショナルクライアント案件の情報セキュリティマネジメントやサーバ実装を実務として担当しており、自分自身を情報セキュリティに関するアマチュアだとは思っていませんが、ペネトレーション・テストを始めとするセキュリティ「技術」の専門家ではありません)が軽率な善意だけで寄せ集めた不十分なコンテンツを量産して素人の注意をバラバラに向けてしまうよりも、話題ごとに見識をもつと見做されている人々のコンテンツに注意を集める方が、コンテンツに対するフィードバックを経て洗練される可能性が高まりますから、結局は情報セキュリティ対策を検討するために効果的なプロセスを期待できるでしょう。アマチュアのブログ記事にフィードバックしたところで、それはせいぜい「もっと本を読め」とか「もっと経験を積みなさい」というていどのことでしかありません(僕は、局所的に些末なフィードバックが繰り返されても、それは「集合知」・・・そんなものがあればの話ですが・・・などにはならないと考えています)。というわけで、一定の能力も責任も期待しうる方々に任せる方がよいわけです。

追記 (2016-02-29)

WordPress の実装もてがけるプライム・ストラテジー社が「『KUSANAGI Ready プロジェクト』発表、徳丸浩氏の顧問就任、吉政忠志氏の取締役就任」という広報を出しており、WordPress 専用の仮想マシンを各社のクラウドサービスに提供するようです。こうしたプロジェクトに関連して、WordPress のセキュリティにかかわる話題も出てくると思うので、参照されるとよいでしょう。

前篇(基礎編)

まずどのような方を対象とするかですが、全ての WordPress ユーザを対象にするつもりはありません。最低限の運用スキルとして、

という条件があります。つまり、自分でレンタルサーバを借りて WordPress をインストールして使っている方というわけです(いちばん上の条件から順番に、それぞれ直後の条件を含意しているように見えるかもしれませんが、そうではないということに注意して下さい)。昨今はレンタルサーバに自動インストールのサービスがあるため、ボタン一発でサイトに入れてお使いの方もおられると思いますが、その場合はセキュリティの責任がユーザ自身にあるのか、それともレンタルサーバ会社にあるのか不明です。もしレンタルサーバ会社のサービスとして、定期的にアップデートされているのであれば、レンタルサーバ会社に対策をご相談ください。自力でセキュリティ対策をすると、レンタルサーバ会社のアップデート作業によって、あなた自身で対策した環境が上書きされてしまう可能性があります。とはいえ、上げ膳据え膳の環境でも導入できそうな対策はあると思うので、自力のインストールでなくても何かの参考にはしていただけるかもしれません。

それから、WordPress の運用に関して私が社内でも何度か質問されていることですが、「PHP を自分でプログラミングできなきゃいけないのか」という点については、もちろん自分でコードを理解し手を入れられたらよいかもしれませんが、ひとくちに「PHP が書ける」と言っても客観的な基準がありません。たとえば僕は Zend Certified Engineer の資格を持っていますが、このようなものは最低限の知識として要求される設問の何割かを正解したという事実を裏書きしているにすぎず、セキュリティ対策を自力で実施できる証明などではありません。また、PHP を実務で使い出してから 13 年ほどになりますが、何年のキャリアだろうとセキュリティ対策がぜんぜんできないプログラマはたくさんいます(僕がそうでないことを祈るばかりですが)。そのようなわけで、前篇(基礎編)ではハードコーディングしなくても導入できる対策に限定して、曖昧な基準のままスキルを求めるようなことはせずに、話を進めたいと思います。

それでも、「このくらいのバックグラウンドがあれば、ハードコーディングを要求される対策にも対応できるだろう」という、何らかの大雑把な指標がほしいというということであれば、「プロダクトリリースしたユーザ管理システムや決済サブルーチンを自力で設計・実装し、httpd などウェブサーバ・アプリケーションの運用も含めてシステムをメンテナンスした経験が3年以上あること」という、ブログを運用するための条件としてはややシビアと思える内容を書かざるを得ません。もちろん、今回のような記事を(不遜にも)書こうとしているくらいなので、僕自身は会員制のサイトで使う決済サブルーチン(決済代行会社との API 通信、データベース接続、システムのメンテナンスを含む)を自力で実装した経験がありますし、できばえはともかく販売管理システムも実装して運用した経験があります。したがって、上記のような条件を満たしている人であれば、最低限このくらいは対策を検討するだろうという見通しは、本稿で立てられると思います(もちろん、それで万全というわけではありません)。しかし、結局のところ、いま述べたレベルのスキルをもっている方であれば、後篇で述べるような対策は自分で調べて既に対応しているでしょう。

限界

次に、現実的な限界について、あらかじめ書いておきます。まず、レンタルサーバで運用している方が対象ですから、どれほどセキュリティ対策として有効であっても、本稿で対象とする方々には不可能と思われる対策は述べません*1。例えば、機密性や予防のために IDS やファイアーウォールを導入すべきとか、あるいは可用性のためにバランサーが必要だなどと言っても、レンタルサーバによってはそのようなオプションサービスが提供されていないところも多いでしょう。だからといって「そのようなレンタルサーバで WordPress を運用すべきではない」などと言う権利は、僕にはありません(言おうとも思いません)。

*1[2015-06-04 の追記] 一例として、株式会社LIGさんのブログにモリイさんが掲載された「WordPressサイトの管理者向けセキュリティ対策方法チェックリスト」を挙げます。モリイさんは WordPress のセキュリティ診断をされているとのことで、この記事は、Coliss をはじめとする海外ブログ記事の引き写し、あるいは「意訳」とか「善意の紹介」と称した無断翻訳にすぎない数々のイカサマブログとは違って、WordPress サイトがどうして狙われるのかとか、攻撃者が自動ツールも使っているとか、初心者にも参考になる内容を書かれています。但し、記事で紹介されている対策の中には「Apacheのhttpd.confにおけるServerTokensを、Prodに設定」するといった、サーバの管理権限をもっていなければできない対策もあります。あなたが専有サーバを借りて root 権限(Windows サーバなら administrator 権限)を持つアカウントを使えるならともかく、レンタルサーバや、専有サーバでも「マネージド」と呼ばれる(丸投げ)プランでサーバを借りている場合は、Apache のようなウェブサーバの設定ファイル(httpd.conf)を編集することはできないので、この点については対策を取れません。しかし、この一点を対策できないというだけで重大なリスクが残るのかと言えば、そういうわけでもありません。

また、「現実的な限界」という言葉の意味としては、技術的な限界だけでなく、もちろん経済的な限界もあります。仮に共用ファイアーウォールのオプションがついていたとしても、レンタルサーバの月額料金が 5,000 円のところで、ファイアーウォールのオプションが月額で +5,000 円もかかるとすれば、ブログの運用予算として受け入れられそうな範囲に収まるかどうかは一概に言えません。それどころか、あなたのサイトだけのためにバランサーや IDS を導入するともなれば、お小遣いや娯楽費としての常識的な範囲を超え、費用は格段に高くなります。

これらに加えて、僕自身が調べたり思いつく範囲の対策でしかないという意味での限界もあります。したがって、ここでは体系的に WordPress でサイトを運営するためのセキュリティ対策を網羅できるとは思いませんし、よくご存知のようにセキュリティ対策は「これで十分、あとは改善の余地なし」という最終の到達点などありませんから、まだ他にも、そして将来に渡っても数多くの対策が可能だと思いますし、必要だろうと考えます。

セキュリティ対策の要約

それでは、これから説明する前篇(基礎編)の対策を列挙しておきます。以下をチェックポイントとしてすぐに理解できる方は、本文を読む必要はないでしょう。

  1. ブログシステムが必要かどうか判断すること
  2. 適正なレンタルサーバを選定すること
  3. 利用する機能を選ぶこと
  4. インストール後の設定を確認すること
  5. セキュリティ対策用のプラグインを導入すること

対策(1): ブログシステムが必要かどうか判断すること

では、さしあたって、WordPress を新規にインストールしようとしている方を想定します。そして、まず考えてほしいのは「そもそもブログシステムを導入する必要があるのか」という点です。WordPress 関連の記事でこのようなことを書くのは身も蓋もないでしょうが、ウェブにコンテンツを公開するときには、事業担当者が業務として行うのであれ、小・中学生が趣味として行うのであれ、リテラシーとしてこのような観点はもっていただきたいと思います。そして、この点はユーザである皆さんだけがやるかどうかを決定できるセキュリティ対策なのです。仮に公開しようとしているサイトが全体でも 10 ページ前後の自社のコーポレートサイトであって、せいぜい更新するとしても決算月の翌月に前年度実績のページを書き換えるくらいであれば、「ブログシステムを導入した」という実績がほしいわけでもないかぎり、はっきり言って無理にブログシステムを導入する必要などありません。自分や自社のリソースでブログシステムを導入したり運用できるのか、そしてブログシステムを導入しなくてもウェブページを更新したり運用できるのかどうか、そういった点を考慮しながら、導入すべきかどうかを決定しておく必要があります。

では、このような点がなぜ「セキュリティ対策」のチェックポイントになるのでしょうか。それは、コンテンツを動的に出力するとか、そしてその仕組みをウェブサイトの運営者がよく理解しているとは限らないという状況を考えると、ウェブサイトで提供しようとするコンテンツによっては静的な HTML ファイルを公開するほうが安全だと言えるからです。もちろん、静的な HTML だけを公開するサイトにも脆弱性や脅威は必ずついて回りますが、不用意にブログシステムを導入することに比べれば比較的安全と言ってよいでしょう。極端に言えば、

<html>
<head>
<title>TEST1</title>
</head>
<body>
<h1>TEST1</h1>
<p>これはテストのコンテンツです。</p>
</body>
</html>

という静的なコンテンツと、

<html>
<head>
<title>TEST2</title>
</head>
<body>
<h1>TEST2</h1>
<p><?php echo $_GET['message']; ?></p>
</body>
</html>

という動的なコンテンツを比較すれば、どちらが脆弱であるかは一目瞭然です。もちろん、WordPress のコアシステムは上記のような愚かなやり方で出力などしていませんが、サイト制作を委託したプロダクションのプログラマが、これと似たり寄ったりのバカなハードコーディングを WordPress のテーマファイルに加えるリスクはありますし、コアシステムにも(これほど単純ではないにせよ)脆弱性が残っているかもしれません。

したがって、セキュリティ対策の第一歩は、あなたの公開するコンテンツが、ブログシステムを使わなければ公開も運用も負荷が大きくなるようなコンテンツなのかどうかを判断することなのです。もしその判断がつかなければ、制作のプロである(筈の)プロダクションに相談すればよいと思います。

他方、もしあなたが中学生や専門学校の学生あるいは定年退職後の方などなどであって、趣味としてブログを運営したいと思っているなら、Dreamweaver やホームページビルダーといったウェブページの制作ツールを買う余裕があるとは限りませんし、HTML やスタイルシートのコーディングを覚える必要を感じているかどうかは分からないので(コンテンツを公開することが第一の目標であるような人たちに、これらの技術的な知識が必須であるとは思えません。プロとして制作している人やプロを目指す人は別ですが)、ブログシステムの導入は「よい選択」の一つかもしれません。必要を感じない PHP や HTML を勉強して自力の運用システムを組んでしまうよりは、既存のブログシステムを運用する方が安全ですし、システムを導入しなくても HTML や CSS のコーディングに負担を感じてコンテンツの作成まで諦めてしまうようなことになれば、元も子もありません。

なお、当サイトは過去に WordPress で運営されていた時期もありますが、上記で述べた理由から、現在は CMS やブログツールを使っていません。

対策(2): 適正なレンタルサーバを選定すること

既にレンタルサーバを借りてしまっている場合は選択の余地が制限されてしまいますが、ブログを運営するとか、コーポレートサイトをブログツールで制作・運営しようと考えていて、まだレンタルサーバ会社を選んでいない方には、情報セキュリティの点から幾つかのアドバイスができます。本稿では「予算がない」とか「そこまでは費用を捻出できない(小遣いがない)」という理由で実現が難しくなる条件を、できるだけ要求しない範囲でセキュリティ対策を考えていこうと思っています。そのため、利用料金がほぼ同じであるとすれば、どういう条件が満たされているほうがよいかという視点で条件を述べておきます。

なお、レンタルサーバを選定する条件として、後篇でもご紹介するポイントがあります。これから述べる説明だけで十分というわけではありませんので、もし後篇でご紹介するポイントも気になる方は「ファイル転送に SFTP や FTPS が使えるかどうか」という条件も合わせて、レンタルサーバを選定されるとよいでしょう。

WordPress のインストール要件は、2015年2月現在では PHP 5.2.4 以上が動作するウェブサーバと、MySQL 5.0 以上で動作している DBMS です(それから WordPress.org へのリンクも!)。昨今の共用レンタルサーバであれば、月額利用料金が数百円の契約価格帯でも、これらの条件を満たしているサービスは多いでしょう。現実的なところでは、月額料金が 3,000 円以下のサービスがよく利用されていると思います。他のブログシステムであれば、もっと違った条件になりますし、もちろん PHP 以外の言語で動作するシステムもありますから、ここではひとまず他のツールと比べて WordPress のセキュリティ対策がどうであるかという話は、範囲が広くなりすぎるのでやめておきます。

同じくらいの価格帯で選ぶという前提であれば、大まかにアプリケーションのバージョンが新しい方がよいと言っておいてよいでしょう。もちろん、ウェブサーバの Apache については、1.x 系統と 2.x 系統のどちらを使ってもよいですし、WordPress を運用するにあたって両者の違いは殆どないと言えます。また、セキュリティという面でも、両方ともアップデートは続けられていますから、1.x 系統が 2.x 系統より前のバージョンだからといって、最新の脅威に弱いとは一概に言えません。MySQL に関しても、5.0 系統、5.1 系統、6.0 系統などと幾つかの違いはありますが、6.x 系統でなければ脆弱だと直ちに決め付けてはいけません。したがって Apache と MySQL については、一定以上の新しいバージョンであれば、新しい系統のバージョンを使っているかどうかよりも、セキュリティホールの警告やバグフィクスに対応したアップデートがレンタルサーバ会社によって頻繁に行われているかどうか(それをきちんとサービスのプロモーションサイトで開示しているかどうか)という点の方が重要です。(もちろん、そういうサーバ会社であれば、それなりに新しい系統のバージョンを導入するとは思いますが。)

PHP に関しては、もうこの記事を初めて公開した頃とは違って、4.x 系統は殆どなくなり、たいていのレンタルサーバでは 5.x 系統が使われていますが、他のサービスや予算の点で違いがなければ、2015年の時点では 5.4.x または 5.6.x 系統の新しいバージョンで運用されているサーバを選択するほうがよいでしょう。もちろん、PHP 本体のソースコードを書き換えて運用するスキルをもっている会社もあるので、一概に 5.x 系統の古いバージョンだから直ちにいけないというわけではありませんが、Zend Engine やモジュールを自力でカスタマイズし続けるコストやリスクと、5.4.x, 5.6.x 系統に移行して運用してゆくコストやリスクを比較すれば、PHP 実行環境の移行という一時的なコストやリスクは必ず発生するものの、現状を維持し続けることに大きなメリットがあるとは思えません。

いづれにせよ、PHP のバージョンが古く、PHP の実行環境にあるセキュリティホールを突かれてもソースコードの改良が行われないのであれば、その上で動作するアプリケーションに対策を講じることが大変難しくなる場合が多く、基本的なビルトイン関数にバグがあると、アプリケーションへのハードコーディングだけでは対処できないこともあります。これからレンタルサーバの契約をするのであれば、古い PHP のまま放置されていそうな会社を選択すべきではないと言えます。

詳しいレンタルサーバの情報は、日本語版の Codex にたくさん紹介されています。

対策(3): 利用する機能を選ぶこと

WordPress を導入すると言っても、全ての機能を使うわけでないなら、不要な PHP ファイルやディレクトリを公開サーバに展開しておくのはよくありません。もしその使ってもいないコードに脆弱性があって攻撃されてしまうと、全く無意味なコストを後で負担しなければならなくなります。WordPress をインストールする際に、公式サイトからダウンロードして解凍したファイルやフォルダを、そのままサーバへ闇雲にアップロードするのは感心しません。

幾つかのケースに分けて検討してみましょう。まずコーポレートサイトを構築する場合、個々の Pages ページ(特に IR コンテンツや会社概要)や新着情報のページに、トラックバックやコメントの機能を導入する必要はないでしょう。そもそも WordPress を使っていると思われるコーポレートサイトで、コメントやトラックバックを使っている企業は見たことがありません。もちろん企業がそうしている理由の一つは、営業妨害に当たる書き込みや株価を下げるような風評を書かれてしまうリスクがあるからです。

たとえトラックバックやコメント機能を使い、上記のようないたずらを防止するため、コメントの表示は管理者の承認が必須であるように設定したとしても、コメントやトラックバックをいちいち管理するという運用コストをコーポレートサイトに投入する意味があるかどうかは意見の分かれるところです。ここからは私見ですが、コーポレートサイトにブログシステムや CMS を導入するケースに限って言えば、コーポレートサイトのコメントを管理するという業務は、カスタマーからのクレームを管理する業務とは違って、企業として行うべき価値も意味も殆どないと考えます。もちろん、ウェブサイトそのものが商品である ASP のスタートアップやオンライン店舗であれば、逆にきちんとフィードバックのしくみを導入しなくてはなりませんが、一般企業のウェブサイトに、問い合わせフォームやメールアドレスのリンクよりも複雑なフィードバックのしくみを導入するのは、おおむね無駄と言えます。したがって、コメントの表示や投稿あるいは管理にかかわる PHP ファイルは、サーバにアップロードする必要はないと考えます。

コメントやトラックバックを一切受け付けない場合、最低でも以下のファイルは公開サーバへのアップロードを除外することになります。

要するに、外からのコメント投稿やトラックバックを拒否すればよいわけです。当然、コメントやトラックバックはデータとして存在しないので、それらのリストを RDF (RSS, XML) として表示するためのしくみも不要です。もちろん、/wp-includes や /wp-admin には、テンプレート用のサブルーチンが含まれるファイルや、コメント管理用のファイルなど、更に除外の対象としうる PHP コードは幾つかあります。潔癖を目指すのであれば、それらを全て取り除き、管理画面用のプラグインを使って、コメントやトラックバック管理の仕組みがメニュー上で見えないようにもできます。

それから、僕自身が利用していなかった機能として、メール投稿があります。当サイトの記事は長文が多く、それなりにまとまった分量の記事でなければ公開しないと決めているので、出先からメールで数行の記事を投稿する場合は Posterous に投稿するという使い分けをしていました(もちろん、2015年の現在は殆ど Google+ に書いているので、この箇所は 2008 年当時の話です)。すると、メールでサーバに記事を送り、WordPress から POP3 プロトコルでメールを読みに行くという機能は不要です。

加えて、メールの機能全般を全く使わない場合は、以下のファイルを除外すればよいでしょう。

このようにケース毎で不要なファイルを除外できるわけですが、一般に、不要な機能の取捨選択にあたっては、特にブログのホームディレクトリ直下にあるファイルを除外することから考えましょう。その理由として、次のように言えます。例えば 2008 年頃の当サイトの場合、ルートディレクトリの直下に wp-rss2.php というファイルがありました。そして、誰でもこのファイルをリクエストできるようになっているので、このようなファイルはリモートから攻撃にさらされることを前提にコーディングされていなければなりません。堅くコーディングされていなければ、それはそのまま誰からもアクセス可能な攻撃対象となってしまいます。

もちろん、WordPress を導入すると決めたからには、自力でプログラミングしたりオリジナルのシステムを開発会社に発注するよりは賢明だと選択したのでしょうから、一定の信頼はあって当然だと思います。しかし、むやみに妄信すると期待過剰となって、何か問題あると容易く「これだからオープンソースは駄目なんだ」といった反動に向かいやすいものです。どのようなシステムにもそれなりに強いところと弱いところがあり、それはプログラミングだけでは解決しないこともあります。ウェブのアプリケーションに限らず、およそ人がつくるものはそうした色々な利点や欠点の組み合わせなのだという冷静な姿勢が望まれます。

情報セキュリティについても、一つ問題が見つかっただけでそのシステムを使わないといった潔癖症では、使えるアプリケーションなど一つもなくなってしまうでしょう。何が最も大切なことなのかを指定し、重要度に応じて、許容できる脆弱性や脅威のレベルを一定以下に保つよう注意して運用し続けることが、リスクマネジメントの基本となります。どこかにある完全無欠なブログシステムを導入すれば攻撃される心配がなくなるとか、予算をこれだけかけたらこれだけのセキュリティが維持されるだろうなどという思い込みは、少なくとも情報セキュリティに関しては、何の根拠もないでたらめな願望でしかないのです。

さて、ダウンロードしてきた WordPress 本体(.zip あるいは .tar.gz という圧縮ファイル)を解凍してみてすぐに気づくことかと思いますが、

上記のうち太字で強調しているファイルは、WordPress を導入する目的・用途に応じて、アップロードしなくてもよいものです。例えば、wp-links-opml.php のようなコードは、頻繁にリンクの一覧をエクスポートするわけでもないなら、必要なときにアップロードして使えばよいだけで、サーバに常設する意味など殆どありません。

対策(4): インストール後の設定を確認すること

インストールが完了すれば、すぐに管理画面へログインすると思います。ここで WordPress のセキュリティを取り上げているブログやサイトで多くの人が忠告しているのは、“admin” というデフォルトの管理ユーザのユーザ名とパスワードを変更しろという一点です(Codex も参照)。そもそも、このインストール手続きが他のユーザや自動巡回ツールに横取りされないという保証など、本当はどこにもないのです。それゆえ、後篇で説明するように、プロダクトリリースのサーバでは公開前にサイト全体にベーシック認証を設定したり、IP 制限をかけたりするわけです。ともあれ、現状ではそこまで厳しいルールは必要ないと妥協して話を進めているため、インストール作業が完了して、”admin” というユーザのパスワードとログイン先のリンクが表示されるところまでステップを経たと仮定しましょう。

真っ先にやることは、彼らが言うには、デフォルトで作られる “admin” という管理ユーザの、せめてユーザ名を変更すること、あるいは新しい管理者ユーザをつくって admin を削除してしまうことです。どちらがよいかは特に大きな違いがないと思いますが、僕は新しく管理ユーザを作ってから admin を削除しています。既につくったユーザのユーザ名を変更するには、MySQL にクエリを送ってユーザ情報を UPDATE してしまうのがいちばんスマートですが、ここでは説明できませんし、新しく管理ユーザをつくって admin を消す方が楽だからです。もちろん、新しくつくった管理ユーザや、admin を改名したユーザについては、パスワードの強度を上げるようにおすすめします。不意に他人のブログの /wp-admin へアクセスして遊ぶような人に容易く侵入されてはいけません。

次に、管理画面で設定しておいた方がよいと思われる点を列挙したいと思います。

以下の内容は、2008年当時の画面をベースに説明していますので、現在は項目が違っている可能性もあります。

まずは「一般設定(General)」ですが、ここでは投稿ユーザを追加するわけでもないかぎり、「誰でもユーザ登録できるようにする」という設定は全く不要なので、チェックされていたら外してください。ただし、その次にある、コメントするにはユーザ登録を要するという設定も、コメントを受け付けるのであれば外さなければなりません(管理サイドしかコメントを投稿できなくなります)。外部のコメントシステム(DISQUS, intensedebate など)を導入する場合は、それぞれの環境に合わせて設定するようにしてください。

次に「ディスカッション設定(Discussion)」では、上記のようにコメントの表示にあたって承認制を導入します。管理ユーザが承認しなければコメントを表示しないようにして、自動投稿をなるべく防ぐために名前とメールアドレスの入力を強制します。まぁ、現在のブログシステム用の投稿ロボットは、このような設定をしても殆ど効果はありませんが。それから、WordPress のセットアップファイルに同梱されている Akismet プラグインはぜひ活用した方がよいでしょう。WordPress.com にユーザ登録すると Akismet 用の API キーが発行されるので、その API キーを Akismet の設定画面で入力するだけでOKです。

上の画像をご覧いただくと、ブラックリストの設定ボックスがあります。ここには、コメントの入力で認めない文字列を登録しておくわけです。WordPress のコアシステムでサニタイズ処理などの不備があるかもしれませんから、ここにも “script”, “alert”, “<”, “>” といった、タグやスクリプトの実行に使われる文字列を登録して拒否しましょう。やろうと思えばかなりのパターンになりますが、考え方としては “script” という文字列を大文字と小文字で記すパターン、”script” それぞれの文字にヌル文字を挟んだパターン、そしてそれらを異なるエンコードで表現しているパターンなど、何らかの「変換処理をして安全にしようとする対策」に対抗する攻撃を、ブラックリストでも拾い上げるという方針を採ることになります。もちろん、この方針は、新しい変換処理が出たり文字の入力間違いで抜け道が発見される可能性が残るという点でリスクは残ります。したがって、サニタイズの処理は文字列を出力する直前で「何を出力しようとしているか」というデータの中身を見て判定しなければ、途中で処理しても無駄と言って良く、場合によっては、そのような処理に依存することが抜け道になってしまう場合さえあります。ですから、ここでの設定は「おまじない」ていどの効果しかない(しかしヘタレ攻撃者への対策にはなる)という自覚が必要です。

それから、いまは前篇としてライトウェイトな対策からご紹介しているわけですが、セキュリティ対策を説明しているブログなどでも見落とされがちな点があり、これはあらかじめ指摘しておく価値があるだろうと思います。それは、コメントの管理画面へうかつに遷移しないということです。

残念ながら、2008年当時のバージョンの WordPress ではコメント管理画面のデフォルト表示が「詳細」になっていました(現在の WordPress 4.1 などでは、そもそもこの表示切替がなくなっています)。とりわけ管理ユーザのアカウントで投稿されたコメントの文面がそのまま表示されます。もし、先のようなコメント入力の規制を突破して XSS などの攻撃を可能にする文字列が投稿されてしまうと、ブログに表示されなくとも管理画面では全て残らず表示・実行されてしまうのです。しかし、いまのところ「リスト」表示をデフォルトとして強制できませんので、何か問題のある文字列を含んだコメントが投稿されると、管理ユーザが投稿画面へ遷移した瞬間に JavaScript が起動するという、リスクの高い状態のままになります。管理ユーザのコメント投稿は管理画面で禁止ワードを指定していようと、そのまま承認されブログに表示されるので、試しに管理ユーザとして <script>alert(document.cookie);</script> とでもコメント欄に入力してみて下さい。

「そのようなリスクがあるとしても、それは自分自身のアカウントが盗まれた上で、そのような投稿がはじめて可能になるのだ」という理由で、いま述べたような指摘を馬鹿げていると反論する人々もいるわけですが、管理者権限をもっているユーザが複数いるブログでは、他の人のアカウントが盗まれていないかどうか常に管理できるとは限りません。そして、そもそも上記のような反論の裏には、「自分自身のアカウントが盗まれたら、誰だってわかるだろう」という、殆ど根拠のない思い込みがあるように思われます。しかし、盗まれた直後は何も変化がなくても(まだ攻撃者はあなたになりすまして何もしていないのですから)、その次に何かをたったいちど行うだけで攻撃者の目的が成就されるなら、あなたは(たとえ何らかの方法でアカウントを盗まれたことに気づくとしても)攻撃者になりすましで何かをされた後でしか気づけないのです。このような状況が、HTML メールを Outlook Express でおもむろに読むことと大差のない、危険な行為であることがお分かりいただけるのではないでしょうか。

繰り返しますが、以下の内容は、2008年当時の WordPress について説明しています。

本来は後篇で説明すべき内容ですが、この点は後篇で再び指摘してもよいくらいの問題なので、具体的な対策を述べておきます。まず、管理画面でコメント管理のページへ遷移したときに、デフォルトで「詳細」表示とならないようにしましょう。それには、ひとまず /wp-admin/edit-comments.php の 49 行目(WP 2.6.3)あたりにある、

if ( empty($_GET['mode']) )
$mode = ‘detail‘;

という箇所を、

if ( empty($_GET['mode']) )
$mode = ‘list‘;

と書き換えます。あるいは「詳細」ビューをそもそも使わないように管理画面そのものを書き換えてしまってもよいでしょう。そして具体的に個々のコメントを見る場合、そこでコメントが表示されるときにも対策を講じる必要があるのは言うまでもありません。

対策(5): セキュリティ対策用のプラグインを導入すること

さて、いきなり話は飛んでインストール後の話になります。実はインストールにあたって wp-config.php のカスタマイズやデータベースの接頭辞を変更することなど、色々とやっておきたい対策はあるのですが、これらはひとまず後篇に回して、インストールした後の運用を考慮した対策に話をすすめます。

よくウェブアプリケーションのインストールについて、インストールが終わった後に掃除しろと書いてある場合があります。WordPress の場合も、確かに /wp-admin/install.php は、インストールが完了すると不要です。但し、現在のバージョンでは、既にインストールが完了したあとで install.php にアクセスしても、

という表示が出るだけなので、ひとまず他人に上書きインストールされるような心配はありません。

既に他所でも紹介されているように、WordPress のプラグインは 2008 年 11 月 15 日の時点で 3,000 を越える数が公開されています。(2015年現在の wordpress.org には、36,000 以上のプラグインが登録されています。ただし、その多くはアップデートされていません。)しかし、これらの中から “security” といった検索で引っかかるプラグインを片っ端から導入していく必要などありませんし、そうすべきでもありません。

この後、セキュリティ関連のプラグインを紹介していましたが、現在は別のものがふさわしいと思われますし、詳しく紹介している書籍やブログ記事もありますので、ここでは古い内容は削除します。

ひとまず前篇でご紹介する対策は以上となります。中にはコーディングを要するものもありますが、リスクをよく自覚して扱えば(あるいは、扱わないと決めれば)、何も考えずに運用しているよりも効果はあると思うので、詳細なコーディングの内容は後篇に回すとしても、敢えて先にご紹介しました。

冒頭に戻る

後篇

適用範囲について

さて、後篇では前篇と同じく共有のレンタルサーバへインストールするというケースを想定します。つまり、レンタルサーバ会社がシステムを用意していて、インストールもアップデートもレンタルサーバ側の代行システムを使って行うような、ASP として提供されている環境ではなく、公式サイトから自分でダウンロードした一式をアップロードするようなケースに限定します(レンタルサーバ側でセットアップしている環境では、サーバの運用も含めてレンタルサーバ会社に責任があり、彼らはここで述べるような対策はプロとして十分に心得ている筈です)。ちなみに、前回は明言しませんでしたが、本稿では WordPress のテーマについては取り上げませんので、ご了承ください。恐らくテーマファイルのプログラミングやコーディングは、PHP でのプログラミング全般にまで話が広がってしまうと思うので、本稿の範囲を超えてしまいます(もちろん、テンプレートタグの取扱い方だけに着目して項目を立てられるとは思いますが)。

前篇では、利用する機能に合わせてアップロードするファイルを取捨選択するとか、主にロギングやチェックを目的としたプラグインをインストールするという、どちらかと言えば初歩的な対策をご紹介しました。後篇では、PHP のコードやサーバの動作をコントロールしてゆきますので、対策が可能なレンタルサーバ・プランが制限されるという意味でも、あるいは対策を実施するために要求されるスキルという意味でも、やや難しくなります。理屈の上ではもっと厳格な対策を加えられるとは思いますが、現実的なラインに妥協するという当初の方針は、たとえプログラミングやサーバの設定に手を入れるくらいの対策までご紹介するにしても、そのまま維持したいと思います。

もっと高度な対策を立てられることは承知しています。しかし、ここで想定しているユーザは、共有レンタルサーバという限られた範囲でしか利用の権利を与えられていない以上、どれほど重要な対策が考えられるにしても、サーバ会社をあるていど信用しなければならないでしょう。したがって、「もっと高度な攻撃にそなえる対策」が可能だからといって、それを「上級レベル」などと称してご紹介するつもりはありません(ただし、他にも対策があるという事実は知っておいて損はないと思います)。内容を「前篇」と「後篇」に分けて、わざとレベルの差という示唆を与えていないのには、そういう理由があります。

セキュリティ対策の要約

さて、今回も以下で述べる内容を箇条書きにして列挙しますが、これと同時にリソースもご紹介しておきます。

リソース

セキュリティ対策の要約(前篇+後篇)

  1. ブログシステムが必要かどうか判断すること
  2. 適正なレンタルサーバを選定すること
  3. 利用する機能を選ぶこと
  4. インストール後の設定を確認すること
  5. セキュリティ対策用のプラグインを導入すること
  6. 適正なレンタルサーバを選定すること(続)
  7. テーブル名の接頭辞を変更すること
  8. 固有のキーコードを設定すること
  9. プラグインで済ませてよいか検討すること
  10. ディレクトリへのアクセスを制限すること
  11. 管理画面へのアクセス制限を強化すること
  12. エラー表示を抑制すること
  13. ユーザの権限を制限すること
  14. パスワードの強度を上げること
  15. システムとプラグインのアップデート
  16. その他

以上のうち、強調表示している項目が後篇の対策となります。

対策(6): 適正なレンタルサーバを選定すること(続)

WordPress 公式サイトに掲載してあるインストール手順は、簡潔で要を得たものになっているものの、実際にファイルをアップロードしてインストールをはじめるには、セキュリティの観点から、他にも考慮しておいてよい点があります。

WordPress のスクリプト一式をサーバへとアップロードするにあたって、そのアップロードという行為に問題がないわけではありません。これは何も WordPress やブログシステムに限った話ではなく、認証を経て何かのリソースへアクセスする場合には、何であれセキュリティを考慮する習慣が必要になってきます。実際、ふだん使っている FTP ソフトだと、途中の経路でパケットをキャプチャーされてしまえば、例えば、

16  2008-11-13 22:37:20.062250  192.168.1.59  219.***.***.***  FTP  Request: PASS ************************

といったデータが丸裸で送信されていることが分かります。これは僕がレンタルサーバに FTP ソフトでアクセスしたときの送信データを Wireshark というソフトでキャプチャーしたものです。言うまでもなく、伏字になっている部分で、FTP サーバの IP アドレスや、FTP アカウントのパスワードが全く暗号化されずに出力されていることが分かります。当然、その前の段階では FTP アカウントのユーザ名もしっかり送信されキャプチャーされているので、この通信をインターネット接続を共有しているマシンで抜き取った人は、僕のアカウント情報を使ってレンタルサーバにログインできてしまいます(念のため、このあとで既にパスワードは変更してあります)。

また、FTP ソフトで転送しているファイルの中身も、当然ながら全く暗号化もされずにサーバへ送信されているので、同じく Wireshark のパケットモニター画面には、

52  2008-11-13 18:37:26.252594  192.168.1.59  ****.jp  FTP  Request: TYPE I
53  2008-11-13 18:37:26.268750  ****.jp  192.168.1.59  FTP  Response: 200 Type set to I
54  2008-11-13 18:37:26.281507  192.168.1.59  ****.jp  FTP  Request: PORT 192,168,1,59,32,202
55  2008-11-13 18:37:26.302330  ****.jp  192.168.1.59  FTP  Response: 200 PORT command successful
56  2008-11-13 18:37:26.314724  192.168.1.59  ****.jp  FTP  Request: STOR wp-config.php
[...]
61  2008-11-13 18:37:26.367483  192.168.1.59  ****.jp  FTP  Request: <?php [...長いので省略]
[...]
68  2008-11-13 18:37:26.395537  ****.jp  192.168.1.59  FTP  Response: 226 Transfer complete.

というログが残り、上記の中で強調した箇所には、しっかりと wp-config.php の中身が記録されていることが分かります。wp-config.php には、通常は秘密にしなければならないデータベースのパスワードや、認証に使うキーコード(key)、あるいは「salt 値」と呼ばれる固有の文字列が定義されていますから、これが他人に知られるのは危険です。

これでお分かりのように、多くの方は何気なく FTP ソフトでサーバにファイルをアップロードしているわけですが、この行為は情報セキュリティの管理者やシステム開発を仕事にしている人たちから見ると、自然なことでもありませんし、ましてや安全な行為ではありません。少なくとも情報セキュリティの管理体制について ISMS(JIS Q 27001:2014)のような認証を受けている企業では、メールに ID とパスワードのペアを書いて平文のメールとして相手に送信するような愚かな行為を禁止するのと同様に、ごくふつうの FTP ソフトでサーバにログインするという行為も禁止している場合が多いのです(逆に、そうしていないシステム開発会社さんやウェブ制作プロダクションさんへは、現状について強く再考されるようお薦めします)。

すると、一般ユーザとしてはどのような対策が可能でしょうか。もっとも簡単なのは、FileZillaWinSCP を使って SFTP でファイルを転送することでしょう。但し、レンタルサーバ側で OpenSSH などを導入し、ssh での接続がサポートされていなければなりません。ところが、低価格帯の共有レンタルサーバではサポートされていないことも多いので、これを導入できるのは一部のサービスだけでしょう。

レンタルサーバによっては、ブラウザでアクセスして利用できる FTP 機能を提供しており、FTPS という SSL で暗号化された FTP 通信によって、ファイルをアップロードできる場合があります。FTPS の場合は、SmartFTP というクライアントソフトも使えます。しかし、これもサーバ側で FTPS に対応している必要があるので、どこでも使える方法ではありません。したがって、まだサーバを借りていないなら、FTPS/SFTP のどちらかがサポートされているレンタルサーバを選択するのはよい考えです。但し、個人で共用のレンタルサーバを借りる場合は、「適正なレンタルサーバを」とは言っても選択肢が限られるので、もっと他に優先したい条件(利用料金や機能)があるのは当然と思います。

対策(7): テーブル名の接頭辞を変更すること

WordPress のインストールは非常にシンプルなのですが、セキュリティの点から何も問題がないというわけではありません。なぜなら、ブラウザ上のインストール処理はすぐに済みますが、その準備として作成する wp-config.php の内容をしっかり確認しつつ設定しなければならないからです。その点、公式サイトにあるインストールの説明は、インストールして動かすまでの手順という大局的な面では要を得ているものの、セキュリティという面から見ると簡略すぎる印象を受けます。

WordPress の書籍を上梓されたフランク・ビュルジ(Frank Bultge)さんのブログなど、多くのブログやフォーラムで指摘されている点として、データベースの接頭辞(prefix)を変更するというものがあります。デフォルトでは、wp-config.php に、

$table_prefix = ‘wp_’;

という設定項目があります。ふつう、これはレンタルサーバでデータベースが1個だけ使えるという環境のときに、複数の WordPress を運用したい人が ‘wp1_blog_’ とか ‘wp2_blog_’ などと、それぞれのブログで使うテーブル名を区別するために、個別に設定する項目だと理解している人も多いのですが(そしてそれは間違ってはいません)、そういう目的がないならデフォルトのままでよいかと言えば、他人に推測されやすいファイル名やディレクトリ名あるいはデータベース名でシステムを運用することは望ましくありません。そして、WordPress のように中身が誰にでも公開されているシステムの場合、デフォルトの “wp_” という接頭辞を変えずに運用すると、データベースへのアクセスは既存の処理に任せて、データの挿入や削除だけを SQL クエリの送信で実行できてしまうようなセキュリティホールが突かれている状況では、攻撃対象となるテーブル名が簡単に推測できてしまいます。というか、変更していなければ推測も何も必要ありませんね。

この状況を詳しく説明すると、まずデータベース(サーバ)にデータを追加したい場合、PHP のプログラムは MySQL サーバに対して接続要求をかけます。ホストやアクセス情報を指定して MySQL サーバとの接続が成功しストリームが開いて実行環境にリソースが確保されると、それ以降は接続を明示的に閉じるか、あるいは PHP の仕様に沿って自動的にリソースが解放されるまで、そのアカウントに付与された特権に応じて、SQL クエリを実行できます。スクリプトの処理が終了すると、データベースへの接続は明示的に mysqli_close() 関数を使えば閉じられますが、あからさまに接続を閉じなくても、スクリプトの実行が終わると、リファレンスカウンティングという Zend Engine の仕組みを使って自動的にリソースが解放されるわけです。すると、最初にデータベースとの接続さえ通常の処理の範囲で成功させれば、その後に SQL 文を実行するような不正コードが挿入されてしまうと、デフォルトのインストール環境では、

INSERT INTO `wp_posts` (`ID`, `post_author`, `post_date`, `post_date_gmt`, `post_content`, `post_title`, `post_category`, `post_excerpt`, `post_status`, `comment_status`, `ping_status`, `post_password`, `post_name`, `to_ping`, `pinged`, `post_modified`, `post_modified_gmt`, `post_content_filtered`, `post_parent`, `guid`, `menu_order`, `post_type`, `post_mime_type`, `comment_count`) VALUES (NULL, ‘0′, ‘0000-00-00 00:00:00′, ‘0000-00-00 00:00:00′, ”, ”, ‘0′, ”, ‘publish’, ‘open’, ‘open’, ”, ”, ”, ”, ‘0000-00-00 00:00:00′, ‘0000-00-00 00:00:00′, ”, ‘0′, ”, ‘0′, ‘post’, ”, ‘0′);

という形式のクエリで、データベース名など分からなくても攻撃者にデータを容易く挿入されてしまいます。上記で強調した “wp_posts” は、wp-config.php で設定した接頭辞と、デフォルトでテーブル名に使われる “posts” との組み合わせに過ぎません。具体的には、WordPress 2.7 beta2 の /wp-includes/wp-db.php を開けば、254行目では次のようになっています。

var $tables = array( ‘users’, ‘usermeta’, ‘posts‘, ‘categories’, ‘post2cat’, ‘comments’, ‘links’, ‘link2cat’, ‘options’, ‘postmeta’, ‘terms’, ‘term_taxonomy’, ‘term_relationships’ );

したがって、’wp_’ の部分がデフォルトのままだとテーブル名を推測するのは非常に簡単です。というよりも、接頭辞と残りの文字列を組み合わせてもデフォルトのままなので、いちいち推測する必要もありません。すると、ひとたびデータベースへの接続が成功している状況で SQL インジェクションのような攻撃を受けると、運用中のテーブルに容易くアクセスできてしまいます。

では、どのように対策を加えればよいでしょうか。具体的には、推測されにくい文字列であればよいので、パスワードを生成してくれるサイトを利用して、ランダムな文字列を 8~10 桁で準備すればよいでしょう。パスワード管理ソフトあるいはブラウザのアドオン(拡張機能)で生成した文字列を使ってもよいと思います。すると、

$table_prefix = ‘5vXrpSFYic_’; // これで、約60ビットの強度

のようになります。

ちなみに、僕が会社でメンテナンスしている WordPress の場合、プロダクトリリースでもありますから、MySQL のテーブル名(識別子)として許容されている最大値の 64 バイトを参考にした桁数でテーブル名をつくっています(今後のバージョンアップで接頭辞の後につく文字列が増える可能性もありますから、現状で最も長くなる「接頭辞」+ “post_content_filtered” からつくられるテーブル名を 64 バイトぎりぎりにしていると余裕がなくなりますから、接頭辞の部分はせいぜい 24~32 バイトくらいで指定すれば十分と言えます)。

なお、MySQL 5.1 以降では、識別子のテーブルに入る文字列はバイト数ではなく文字数でカウントされているため、マルチバイト文字(日本語など)でテーブル名を作っても「テーブル」は4文字とカウントされますが、MySQL 5.1 で運用されている安価なレンタルサーバはそれほど多くないと思われますし、識別子にマルチバイト文字を使うのは、現状では一般的な習慣とは言いがたく、ファイル名や関数名にマルチバイト文字を使える環境も限られていますので、ISO-8859-1 Latin1 に沿って、手堅く半角の英数文字だけを使っておくほうが無難です。

もちろん、そのように対策して設定した wp-config.php を、先にご紹介した SFTP のような方法でファイル転送しない限り、どれだけ文字列の推測可能性を下げるように設定しても無意味になるかもしれません。PHP のコーディングで対処できることには限界があるという、よい事例になるでしょう。

対策(8): 固有のキーコードを設定すること

WordPress のアップデートに際して、テーマやプラグインや wp-config.php を除くシステムファイルを単純に上書きだけしているような豪快な方は、wp-config.php に取り込まれている変更に気づきにくいと思われます。ですが、アップデート情報と共に変更点を確認されている方はお気づきのとおり、現行のバージョン(2.5 以降)では 3 種類のキーコードを wp-config.php で設定するようになっています(追記: 2009-01-25: バージョン 2.7 からは “NONCE_KEY” というキーコードも追加されました。今後はバージョンアップに伴って本稿を更新したりはしませんので、ご注意ください)。

define(‘AUTH_KEY’, ‘put your unique phrase here’); // 固有の語句に変更してください。
define(‘SECURE_AUTH_KEY’, ‘put your unique phrase here’); // 固有の語句に変更してください。
define(‘LOGGED_IN_KEY’, ‘put your unique phrase here’); // 固有の語句に変更してください。

2015年の時点では、更に増えています。

  • define('AUTH_KEY', 'put your unique phrase here');
  • define('SECURE_AUTH_KEY', 'put your unique phrase here');
  • define('LOGGED_IN_KEY', 'put your unique phrase here');
  • define('NONCE_KEY', 'put your unique phrase here');
  • define('AUTH_SALT', 'put your unique phrase here');
  • define('SECURE_AUTH_SALT', 'put your unique phrase here');
  • define('LOGGED_IN_SALT', 'put your unique phrase here');
  • define('NONCE_SALT', 'put your unique phrase here');

上記の設定は、日本語版の WordPress に入っている wp-config-sample.php (wp-config.php の雛形)から拾ってきた内容です。これらはハッシュ値を導き出す際の salt 値を与えており、サイトに固有のハッシュ値を出力するためには、上記の定義値をデフォルトの文字列ではなくオリジナルの文字列にしておく必要があります(厳密に言うとハッシュ関数の演算結果は異なる文字列から作ったハッシュ値が同じになってしまうという衝突の可能性をもつのですが、可能性を下げることはできます)。もちろん、デフォルトのままでも md5() や sha1() を使って wp_rand() ルーチンの中でハッシュ値は計算されますし、古いバージョンで使っていた wp-config.php のまま LOGGED_IN_KEY が定義されていない場合ですら、勝手にハッシュ値は計算されます。しかし、salt 値を導入しないと、パスワード解析のいわば「過去ログ」を使う、レインボーテーブル攻撃への耐性が下がってしまうと言われています。また、デフォルトの salt 値を使っていると、ハッシュ関数の引数のうち salt 値の部分は既に攻撃者にはバレてしまうので、salt 値を使う意味が殆どなくなってしまいます。実際、これらの salt 値が wp-config.php に設定されていることに気づかなかったとか、salt 値が三種類に増えていたことを知らなかったユーザがデフォルトの salt 値を使い続けるといったことが、WordPress のセキュリティ上の問題として広く語られていた時期もありました。

このような次第で、上記の設定項目には(少なくとも、デフォルトの文字列とは別の)オリジナルな値を設定しましょう。この場合も先の節で説明したテーブルの接頭辞と同じく、他人に推測されにくくて、それなりに長い文字列を設定すればOKです。理論値については、Ivan Lucas さんのサイトで紹介されていますので、興味がある方はご参照ください。僕の場合は、パスワード管理ソフトに付属しているパスワード生成機能を使って、64バイトていどの文字列を生成して使っています。なおデジタルコンピュータは、例えば「1から10までのあいだで任意の値」といっても、コンピュータが現実に扱える有理数の範囲でしか値を弾き出せません。WordPress においても、過去に 2.6.2 へのアップデート内容が mt_rand() 関数を単純に(salt をユニークに設定しないで)使っただけでは擬似乱数としての強度が高くないという点にあったこともあります(日本語の解説としては、大垣靖男さんの記事が参考になります)。

define(‘AUTH_KEY’, ‘SxNVv8atKcU46UWPbACaMoqlgkhPiQmBM3AwgHE7qzWLUed395H4Q_oXy-no9GKh’);
define(‘SECURE_AUTH_KEY’, ‘1s1yVw_4EPQy-r8_EBtFSMv_o8KwRs_zfocte4JHCz_J8mws3hCPWiIlPB9LX1UI’);
define(‘LOGGED_IN_KEY’, ‘oDtyeg8qPwk0Ta9XkSDsQocVaxEL6e9xZW4_JiiVDnK0wpzaHlICXNMzd46hgBnh’);

パスワード生成機能を何度か使って、強度が 300 ビット以上になるようにしています。もちろん、桁数は同じでも更に幾つかの記号を文字列に使えば、salt 値の強度を上げることは可能です。パスワード管理ソフトやパスワード生成ソフトを使っていない方は、WordPress.org の SECRET_KEY ジェネレータで出力してみるとよいでしょう。

追記: 11/21

キーコードを三つとも出せるジェネレータもあります。SECRET_KEY は古いバージョンのキーコードなので、現行のバージョンでは使えないことに注意しましょう。なお、どちらを参照するにしても、HTTPS では接続できない URL なので、表示されたキーコードをそのまま使うのは遠慮するほうがよいでしょう。

追記: 12/13

本日公開された WordPress 2.7 からは、上記のキーに加えて “NONCE_KEY” という設定もあります。wp-condig.php をご確認頂いて、こちらも同様にユニークな文字列を指定して下さい。

2015年の時点で最新のキー生成ジェネレータは、ここを使えばよいでしょう

対策(9): プラグインで済ませてよいか検討すること

どうしてこのような項目を加えたのか、疑問に思うかもしれません。そういう方には、ここで述べているセキュリティ対策が、個々の具体的なプラグインの紹介であるとか、あるいはコーディングの紹介をしているように見えても、本来は「攻撃ベクトルを認識して、対策の指針を考えましょう」という提案を含意しているのだという点を、ご理解いただきたいと思います。ここまでにご紹介した、テーブルの接頭辞を変更するという対策や、wp-config.php の salt 値をユニークに設定する(もちろん厳密にはユニークではないので、少なくともデフォルトから変更するということ)という対策には、実はどちらもその対策を実行してくれるプラグインが存在します。しかし私見としては、そのようなプラグインを使うことには異論を述べておきたいと思います。

この後篇に関心をもってお読みいただいている方は、.htaccess や PHP スクリプトの編集もあわせて、セキュリティ対策を検討されているのだと思います。そして、そのような方であれば、wp-config.php をテキストエディタで開いて編集することと、wp-config.php をプラグイン経由で編集することを比較してみたときに、前者の運用負荷が格段に大きくなるとは思えません。テーブルの接頭辞については、インストール時に wp-config.php を編集すればよく、部外者から推測されにくくなるからといって、管理画面へアクセスして記事を書くたびにテーブルの接頭辞を変更するような人はいないでしょう。頻度という点から言って、せいぜいその程度の回数しか行わない対策のためにプラグインを導入するというのは、あまり効果的とは言えません(こういうことを書くと誤解する人がいるかもしれませんが、僕は他人の善意とか努力を「無駄である」とか「余計なお世話だ」と、問答無用に撥ね付けているわけではありません)。

そして、幾つかのブログでも指摘されている点ですが、プラグインを使ってファイルを更新(ファイルの中身を書いたり読んだりする)するという仕組みそのものにもセキュリティ対策が必要となってしまいます。単純に言って、完全無欠なセキュリティ対策を施しているプラグインというものがあるとは思えないので、管理用の設定ファイルを読み込んだり上書きするという重要な作業に、新しい脆弱性が加わる可能性を排除できない以上、そのような目的にはプラグインを利用すべきではないだろうと考えます。もちろん、PHP のコードが分からないという方は、そういうプラグインを使わざるを得ないかもしれませんが、それでもツールを使って設定ファイルを読み書きしていることのリスクは理解してほしいと思います。

また、WordPress において実行されるアクションやファイルの更新履歴をトレースするようなプラグインについても、公開されているプラグインはわたしたちだけでなく攻撃者にも動作原理を明らかにしているのです。もし弱点があれば、プラグインによって記録される履歴情報を偽りつつ不正なアクセスを試みるプログラムが作成されるかもしれません。

対策(10): ディレクトリへのアクセスを制限すること

WordPress のセキュリティ対策として頻繁に提示されるパターンは、大きく言って「ディレクトリアクセスの制限」、「権限の制限」、そして「通信経路の暗号化」となります。つまり、WordPress のコアシステムを改変するという PHP のコーディングではなく、ウェブサーバの挙動をコントロールすることに重点が置かれていると考えられ、これは WordPress というシステムをアップグレードしてもセキュリティ対策の効果が維持されやすいというメンテナンス性にも寄与します。そこで、次にディレクトリアクセスを制限する方法をご紹介しましょう。

ディレクトリへのアクセスを制限する方法は、/wp-admin について加える制限と、/wp-content, /wp-includes について加える制限とで、細かい点が違います。まず、/wp-admin つまり管理用のファイルが入っているディレクトリへのアクセスを制限するには、WordPress の管理ユーザがどのような条件で管理画面にアクセスしているのかを特定しておく必要があります。或る決まったマシンやネットワークセグメントからだけアクセスするなら、例えば自宅で使っているマシンや会社で使っているマシンからだけ管理画面にアクセスするというケースに限定して、アクセスを幾つかのやり方で制限できます。

具体的には、.htaccess に IP アドレスを指定して /wp-admin へのアクセス元を制限します(俗に「IP 制限」と言われます)。例えば、

Order deny,allow
Deny from all
Allow from 202.***.***.***
Allow from 168.***.***.***

のように記入した .htaccess というテキストファイルを /wp-admin 直下にアップロードして、202.***.***.*** と 168.***.***.*** からのアクセスだけを認めるようにすればOKです。

但し、アクセス元の IP アドレスを制限してしまいますから、不意に友人の自宅から記事を書きたいときや、何か緊急の必要があって出先から管理画面を使いたいとしても、許可している IP アドレスとは異なるアクセス元からのアクセスになるので、管理画面は使えません。もちろんそのときに SFTP でサーバにアクセスできれば、一時的に .htaccess を削除するかリネームすればよいわけですが、あとで必ず現状復帰しておく必要があります。SFTP (SCP over ssh) でのアクセスも IP アドレスで制限している場合は、社内、あるいはアクセスできる場所にいる誰かに頼むしかありません。

なお、「自宅マシンの IP アドレス」とか「会社マシンの IP アドレス」と言っても、その調べ方を正確に知らなかったり、.htaccess に記述するときの書式を正確に知らなければ、自分自身が管理画面から締め出されてしまうことになりかねません(笑)。一般に、かなり小規模の会社でも、ネット接続を社内で共有している限り、個々のマシンはゲートウェイの役割を果たす機器から DHCP によるローカル IP アドレスの割当てを使ってぶら下がっている場合が多く、ゲートウェイのグローバル IP アドレスはなかなか教えてもらえないかもしれません。また自宅のルータが ISP から貸与されているものであれば、多くの場合に IP アドレスは一定の期間にわたって契約者へ「貸し出し」されるという体裁をとるため、何日か経ってルータを再起動したら IP アドレスが変わってしまっているということもありえます(高木浩光さんの記事が参考になります)。したがって、IP 制限を安定して運用するには、インターネットに接続するためのゲートウェイになる機器へ割り振られた IP アドレスが固定されている環境が最適と言えるので、固定 IP アドレスを取得できる接続契約をしている方は、ぜひ IP 制限を検討してみてはいかがでしょうか。

もちろん、IP アドレスが一定期間ごとに動的に割り振られるような接続環境でも、ISP が使っている IP アドレスの数には制限があるため、サブネットマスクを指定して、動的に割り振られる可能性がある IP アドレスの範囲からくるアクセスをすべて許可してしまうこともできます。ただし、この場合は、あなたと同じプロバイダと契約してネットに接続している人から攻撃されても対処できません(もっとも、まともなクラッカーは自分の使っている IP アドレスが直に判明するようなやりかたでアクセスしたりはしないものですが)。

次に、/wp-content, /wp-includes へのアクセス制限ですが、どちらも JavaScript ファイルやテーマのスタイルシートファイルが入っているので、単純に .htaccess で Deny from all にしてしまうと、ページが正しく表示されません。そこで、.htaccess を使って全域に制限をかけてから、例外のルールを追加します。

Order allow,deny
Deny from all
<FilesMatch “\.(css|jpeg|jpg|png|gif|js|swf)$”>
Allow from all
</FilesMatch>

ブラウザからのアクセスを認めるファイルの拡張子を正規表現としてマッチさせて、該当するファイルをアクセス制限から除外します。もちろん、他に /wp-content に .txt や .doc など公開したいファイルがある場合は、それらの拡張子も登録しておきます。なお、<FilesMatch> のように記述する命令は「ディレクティブ」と言いますが、これには上記のように FilesMatch と Files の二種類があり、2.x 系以降の Apache では条件に正規表現を使うなら FilesMatch の方が推奨されています。それから、/wp-content と /wp-includes ではアクセス制限から除外すべきファイルの種類が違う場合がありますので、ご注意ください。特に /wp-includes は上記のままだとリッチテキストエディタが動作しなくなる可能性があります。

対策(11): 管理画面へのアクセス制限を強化すること

さて、/wp-admin はブログをリモートからコントロールする管理画面を提供するので、更にアクセスを制限する方法が提案されています。しばしば、「多重レイヤー防御 (multi-layer protection)」と呼ばれており、ここではその一つとして、通常のログインに加えて /wp-admin ディレクトリへのアクセスにベーシック認証を導入するという対策を検討します。

既にご存知の方には言うまでもないことですが、ベーシック認証を導入すると /wp-admin 配下のファイルへアクセスする全てのリクエストに認証ステータスが要求されます。したがって、画像や JavaScript スクリプトファイルが一個だけ /wp-admin 配下にあって、ベーシック認証を要求しないウェブページでそれを読み込んでいるという場合でも、認証を要求する小さなウィンドウが出てきます。WordPress の管理画面へログインするときのページは、WordPress のシステムが動作させている認証のしくみなので、PHP の動作と関係のない別の認証方法を組み合わせると、WordPress のシステムに問題があったとしても、それだけでは管理画面に入れません。これが多重レイヤーと呼ばれる理由です。もちろん、ベーシック認証では Base64 にエンコードしただけで ID やパスワードをサーバへ送信するため、先に述べた FTP の場合と同様に、途中の経路でパケットを盗聴されてしまえば危険であることに変わりはありません。したがって、更に強い条件で認証を行うには、SSL 通信を使うことが望ましいと言えます。

しかし共用のレンタルサーバでは、SSL 通信を提供している場合でも、SSL で使うサーバの証明書はドメインに対して発行されるので、サーバを共有する多数のユーザに一つずつ証明書を配布することはできません。最近では SSL のサーバ証明書は安くなってきているとはいえ、それでも有効期限1年のもので3万円~9万円します。したがって、SSL 通信を使うときだけのドメイン(よく、’sv06.domain.com’ などと通し番号になっているドメイン。そのサーバに何百、何千という契約者の領域が割り振られています)でサーバの証明書を取得して、個々のユーザにはそのドメイン配下のディレクトリを ’sv06.domain.com/user01′, ’sv06.domain.com/user37′ などとして使ってもらうパターンが多くなります(このあたりの具体的な話は、自分のマシンに Apache, OpenSSL, Bind といったソフトウェアをインストールしてみて、ウェブサーバや DNS サーバの設定を自分自身でやってみることが効果的です。面倒を厭わなければ全て無料で手に入りますし、セキュリティ対策の知識も格段に上がることは保証できます)。このような利用条件だと、WordPress の管理画面だけを SSL 通信が可能な領域に移動させるのは難しくなります。

追記: 2008/12/27

なお、/wp-admin にベーシック認証を設定すると、記事の編集画面で使う FLASH 版のアップローダで画像をアップロードするときに認証ウィンドウが出ます。そのままアップロードできるときはよいのですが、筆者の環境ではブラウザがフリーズするため、セキュリティを考慮している方は、ベーシック認証の方を諦めるのではなく、FLASH アップローダの方を諦めましょう。こちらには HTML 版という代替があるからです。

対策(12): エラー表示を抑制すること

この対策は、PHP の開発全般あるいはウェブサーバの設定という話題でも目にする方が多いでしょう。ブログやウェブサイトへアクセスしたときに、MySQL の接続エラーや PHP のエラー、あるいは Apache の Internal Server Error の画面をご覧になったことがあるかもしれません。一般に、このようなエラー画面をリリースサーバ(または「プロダクション・サーバ」とか「公開用サーバ」あるいは「本番サーバ」などと、会社によって呼び方は色々とあります)で表示するのは、エラー画面に出力される情報が攻撃者へのヒントになるので、好ましくないとされています。

ERROR 2002 (HY000): Can’t connect to local MySQL server through socket ‘/var/lib/mysql/mysql.sock’ (2)

これは、MySQL に接続したときのソケットファイルにアクセスできないというエラーです。別のバージョンの MySQL を入れて複数起動してしまったとき(古いバージョンが動いていて同じソケットファイルへアクセスし続けている)や、ソケットファイルを root で手動作成したとき(mysql というユーザの権限では開けない)、そういった場合に出るエラーです。

Notice: Call to undefined function hogehoge() in /var/www/public_html/hoge.php on line 3

これは特に説明の必要はないと思いますが(冗談で出力させたエラー表示ですので)、hogehoge() などという関数はないというエラーです。もちろん、ユーザ定義関数として作れますが、この hoge.php には全く定義を書いていませんから、エラーが出るのは当然です。しかし問題はそういうことではなく、

といった各点にあるわけですね。開発用のテストサーバ(「ステージング・サーバ」など、これも会社によって呼び方が違っていたりします)で動かすときは詳細なエラー表示は便利かもしれませんが、リリースサーバの運用にあたっては、攻撃されやすくなるヒントは与えるべきではありませんから、こうした表示は出ないように配慮したほうがよいでしょう。具体的な対策としては、

この二つを行います。もちろん上記のやり方そのままだと、どちらもサーバをコントロールできる強いユーザ権限が必要になってしまいます。そのため、共用のレンタルサーバを借りているユーザさんは、条件が許されるなら、上記の設定を .htaccessで行うことになります。「条件が許されるなら」というのは、.htaccess が使えない共用サーバもあるからです。もし .htaccess の利用が認められているなら、

ServerSignature Off
php_flag display_errors Off
php_flag display_startup_errors Off

を、WordPress のホームディレクトリにある .htaccess へ追記すればよいでしょう。なお、Apache の設定項目には「コンテクスト」と呼ばれる条件があり、Apache のディレクティブをなんでもかんでも .htaccess に書けるわけではありません。PHP のディレクティブにしても同じで、php.ini でしか設定できない項目もあります。ServerSignature は、.htaccess に書けるコンテクストがあること、そして display_errors は PHP_INI_ALL のコンテクストにあって php_flag で設定可能なので .htaccess へ記述して使えるわけです。それから PHP の起動シーケンスでエラーが起きると、実行時のオプションとして display_errors が設定されていても PHP のエラーが出力されてしまうので、display_startup_errors も Off にするよう推奨されています。

また、この手の設定スウィッチを PHP スクリプトの中に ini_set() で書く人もいますが、そのスクリプトがコンパイルエラーなどを起こすと、そもそも ini_set() が実行されないので意味がありません。また、header.php などとして全てのファイルで require_once() しなくてはならないという点でもメンテナンス性が低いと言えるでしょう(.htaccess で設定すれば、新しい PHP スクリプトがどれだけ増えても何もしなくてかまいません)。

対策(13): ユーザの権限を制限すること

これは、ブログの記事を書くような作業であれば「制限ユーザ」で行うことという、OS のユーザ管理と全く同じ理由で提案されている対策です。もちろん WordPress をインストールするときは管理者権限のユーザを使うわけですが、ふだんの記事投稿に管理者権限が必要とは思えません。必要かつ最小限の権限で運用することが適切です。

この話題については、WordPress 本体のユーザ権限と、データベースのユーザ権限に分けて提案されています。WordPress をいちユーザとして使うときは権限を弱くするという課題については、ユーザ権限を指定して「制限ユーザ」を作れば簡単に実現できるでしょう。せいぜい「編集者(editor)」か「投稿者(author)」にしておけばよいと思います。あるいは、更に細かく「役割(role)」を設定して、オリジナルのパターンで権限をもったユーザをつくってブログを運用したい場合は、下記のように細かく権限を設定できるプラグインも公開されています。

次に、MySQL のユーザにも権限の概念があるため、インストールが完了してからは(プラグインを追加するのでない限りは)テーブルやデータベースを削除するといった強い権限は不要でしょう。専有サーバやホスティングと比べて、共有レンタルサーバのユーザにどこまでの権限があるかはサービスによって違うと思いますが、最低でも作成したデータベースに操作の範囲を制限されているはずです。すると、データベースそのものを削除するような権限までは持っていないと仮定して(つまり、レンタルサーバのコントロールパネルで DB ユーザをつくったときに、そのユーザと root だけが権限を付与されている、ユーザに固有のデータベースが自動的に作成されるような仕様になっていると仮定して)、WordPress つまり PHP の mysqli_* 関数で行えたりクエリを実行できる範囲を、不要であれば狭くしていくという対策をとることが望ましいと言えます。ただし、共用のレンタルサーバで作成される DB ユーザには、ユーザの権限そのものを設定するような権限(Privilege)は与えられないのが当たり前とも考えられるので、WordPress の管理者やサーバの FTP ユーザではなく、レンタルサーバのコントロールパネルへアクセスする権限で DB ユーザの権限を制限できなければ、あまり効果的ではないでしょう。上記のような、WordPress のユーザの役割を制限できたからといって、攻撃者が勝手に DROP TABLE(テーブルをきれいに削除してしまう命令)のクエリを投げるのを止めさせることはできないからです。ただし、これは共有のレンタルサーバという条件で考えているだけなので、もちろん mysql の root 権限までもっている方は、きちんと WordPress のデータベースにアクセスするデータベース・ユーザの権限を制限しておくべきです。

対策(14): パスワードの強度を上げること

これも WordPress の運用に限った話ではありませんが、WordPress のユーザについては、権限の弱いユーザで運用するときは自分にとって覚えやすいパスワードを設定しておいて、管理ユーザには強いパスワードを設定しておくという区別をしておく場合もあると思います。逆に言うと、ふだんから使うアカウントは利便性を考慮して比較的弱いパスワードを設定するからこそ、権限を弱くしておく方がよいのだとも言えるでしょう。それ以外の、いちど設定すれば頻繁にログインしないアカウントは、できる限りパスワードの強度を上げておきたいところです。

では、具体的にパスワードはどのていど強くすればよいのでしょうか。これは利便性を考慮すると、権限の弱いユーザについてはなかなか一口に言えませんが、それでも10桁以上の大小英数文字をランダムに組み合わせて作ることが望ましいと思います。_)(*&^%$><”:} といった様々な記号を織り交ぜられたら更に強度は上がります。権限の強い管理ユーザについては、暗記などしなくてもパスワード管理ソフトなどで記録しておけばよいので、大きな桁の文字列で設定しておくようにしましょう。なお、パスワードはデータベースの user_pass フィールドに varchar(64) で定義されていますが、これはメッセージダイジェスト(ハッシュ化した結果の値)の桁数なので、パスワードの桁数が最大で 64 という意味ではありません(試しに 128 桁でパスワードを設定してみるとよいでしょう)。

ご参考までに、僕が勤めている会社で運用しているルールを一部だけご紹介すると(僕自身が起案しているわけですが)、FTP や SCP over ssh アクセスに関してはユーザ名を 16 桁のランダムな英数文字列、パスワードは 32 桁のランダムな英数文字列で設定しています。特に、ホームディレクトリ設定で最上位のディレクトリパスを制限していないアカウント(いわゆる admin とか web など)は、文字の種類に記号などを含めて桁数を更に増やしています。逆に、案件で一時的に利用するディレクトリをホームにしているアカウントは、16 桁ていどのパスワードにして案件が終了するまで貸し出します。

強度にずいぶん差があるという印象をもたれるかもしれませんが、私見では情報セキュリティの管理策はむやみやたらとセキュリティレベルだけを上げることが目的ではないと思うのです。もちろん、社内の不平不満を抑えることよりも、クライアントや株主あるいは情報を登録している一般ユーザの利益を優先して厳格なルールを立てる場合もありますが、だからといって可用性や事業継続性という観点を無視して策定するような規程には、企業のルールとして殆ど価値がありません。情報セキュリティにかんする意識や規程は、現実的な管理策や牽制を図りながらバランスを考えて徐々に根付かせていくしかないと考えます。

なお、パスワードの作り方については、先にテーブルの接頭辞を変更する対策などでご紹介しているので、ここでは割愛します。

対策(15): システムとプラグインのアップデート

これはもちろん、情報セキュリティを扱っている多くのサイトやブログで推奨されている対策です。WordPress 本体やプラグインがアップデートされたら早めに反映させましょうという点では、特に問題なく同意できます。ただ、アップデートの方法については、先にプラグインを使うことの是非を述べた理由と同じく、オンラインでの自動アップデートを行う場合は、管理画面に FTP のアクセス情報を登録するということのリスクを改めて検討しておくべきだと思います。もちろん、サーバ側でサポートされている場合は、

この自動アップデート画面では、いちばん下の「SSL を使う」という設定を有効にしましょう。

対策(16): その他

さてここまで色々な対策を見てきましたが、もちろんこれらの他にも対策は提案されています。

例えば、「ディレクトリ・リスティング」と呼ばれる状態を防ぐために、ダミーの index.html をアップロードするという対策があります。ディレクトリ・リスティングは、Apache の mod_autoindex モジュールがロードされていて Options ディレクティブの設定に -Indexes が指定されておらず、加えて DirectoryIndex ディレクティブに指定されているファイルが存在しなければ、mod_autoindex がディレクトリ内のファイルをリスト表示するという動作を指しています。Apache の動作としては全く正常な動作ですし、こういう動作が必要とされる場合もあるわけですが、必要なければディレクトリ内のファイルが一覧できてしまう事態は避けたいものです。例えば、フレームワークを導入してウェブアプリケーションを動かす場合、mod_rewrite を使ってセグメントベースのアクション呼び出しを行わず、フロントコントローラからのディスパッチだけでアクションを呼び出しているとしましょう。コントローラやビューのスクリプトを格納するディレクトリはドキュメントルート以下に置かず、ディレクトリ名も難読化するのが開発の常識ですが、難読化しているからというだけでドキュメントルートの配下に置いてしまう人もいます。しかし、ダミーの index.html もなく Indexes が有効になっていると、ディレクトリ・リスティングによってディレクトリ名が簡単に分かってしまう可能性もあります。

これではディレクトリ名を難読化した意味がなくなります。(もちろん、不特定のユーザがアクセスできる場所にディレクトリを置いているという時点で、対策としては気休めていどの意味しかなく、このような条件では難読化は「隠蔽」にはなっていません。ちゃんとやるには Alias などで全く別のパスにあるディレクトリを参照する必要があります。)

WordPress の環境では、/wp-includes, /wp-admin はおおむね誰の環境でも同じと言えますが、/wp-content/plugins がディレクトリ・リスティングの状態になっていると、どういうプラグインを使っているか(アクティブかどうかは分かりませんが)が分かってしまい、脆弱なプラグインを使っていれば攻撃の的になるかもしれません。理由は分かりませんが、2.6.3 でもここにダミーの index.html は入っていないので、もしお使いの環境に入っていなければアップロードすることをお薦めします。中身はなくても構いません。

次に、wp-config.php が重要なファイルであることは既に何度も述べましたが、対策(10) の応用として、.htaccess で wp-config.php へのアクセスも制限しようと言われているので、これは WordPress のインストールディレクトリ直下にある .htaccess へ、

<FilesMatch “^wp-config\.php$”>
deny from all
</FilesMatch>

という記述を追加すればよいでしょう。

それから付け足しの話題として、コアシステムへの「ハック」を安易にやるなというアドバイスを書いているブログがあります。これについては、あなた自身の PHP やセキュリティに関する知見に依存するので、本稿が対象としている共有レンタルサーバのユーザを対象として指針を提案するなら、同意せざるをえません。したがって、本稿でご紹介すべきセキュリティ対策には、コアシステムの改変は含めないことにしたわけです。もっとも、WordPress を使っているユーザの大半はコアシステムに手を入れたりはしないでしょうし、スクラッチから PHP で書けるテーマ作成の方がよほど危険だと言えます。

冒頭に戻る

最後に

ここまでにご紹介した対策の他にも、理屈として考えられる対策だけでなく、共有のレンタルサーバでも実効性のある対策はたくさんあります。中でも、今回は WordPress のコアに手を入れるような対策はご紹介していませんので、不十分だと感じる方もおられるでしょう。それから、テーマの作成や導入にあたって注意したい点についても、もちろん言うべきことは数多くあるのですが、これらも今回は割愛しています。

ここでご紹介した対策だけでも色々とあるわけですが、要点としては具体的にコードをどう書くとか、どういうプラグインを使うということが言いたかったわけではありません。あくまでもセキュリティ対策を導入するときの指針として、みなさんのブログやウェブサイトについて、機密性を一定のレベル以上に保つべき部分はどこなのか、どういう機能を完全性や可用性の点から安定して使えるべきなのかという点を考慮して、リスクの切り分けや評価をしていただくよう提案したかったのです。

「ウェブサイト」と一口に言っても、その構成を理解するには幾つかの枠組みがあります。サーバに入っているファイルの機密性という尺度で個々のファイルに重みをつけてゆくこともできれば、ビジターに認知してもらいたいプッシュコンテンツの重要度という尺度でページやコンテンツに重みをつけることもできます。今回は情報セキュリティという観点から、ウェブサイトのベースになる WordPress というシステムを取り上げていますが、取り組む相手がよく分からないシステムであろうと、馴染み深い画像ファイルあろうと、結局はそのサイトにとって何が重要なのかを決めるのは運用者やクライアント自身です。セキュリティという枠組みの中でウェブサイトの安全性を評価するときにも、個々のセキュリティ対策がウェブサイトにとって本当に必要かつ重要なところを適正なレベルで防御していることが前提です。どれだけファイアーウォールのポリシーを堅くしたり、突破が難しい文字列操作のライブラリを導入しても、運用者がドキュメントルートに個人情報の書かれたエクセルファイルを置いていては、セキュリティポリシーの多くは無駄となります。(このような、どれほど技術的に高いレベルで防御していても、ユーザの行動一つで台無しになってしまう状況を "no game"(没収試合)と呼ぶ場合があります。)

WordPress については、有名でシェアも大きくなったので、色々な人が色々な「べからず集」を掲載しています。その多くは単なる素人のコピペや無断翻訳の類であって、評価には値しませんが、中には気づかなかった点を指摘してくれるという意味で有用だと言えるページもあります。しかし、何を守るべきで、自分たちの運用するウェブサイトの価値はどこにあるのかを、運用している当事者が理解していなければ、セキュリティ事故というものは簡単にはなくならないと思います。ウェブサイトの運営に責任をもつ企業の方や、自分のブログを運営したいと考える方には、細かいノウハウをかき集めることに時間を使うよりも、ウェブサイトを公開したり運用するために必要な判断や評価を行うという、情報セキュリティの技術者が代わりにやれないところから考えてみましょうという提案でもあったのです。

今回も長いエントリーになりましたが、誤った説明や、逆にリスクを高めてしまう対策とか意見を書いているかもしれません。「これはよくない」と思う箇所がもしあれば、コメントをいただければありがたいです。

冒頭に戻る

追加レビュー

本稿の内容を、公開した後に出てきた新しい情報と比較しています。もちろん、あからさまに本稿が間違っていたと分かった場合は、本稿の内容を修正します。

Acunetix, "WordPress Security: Top tips to secure your WordPress Application," 2015:
http://www.acunetix.com/websitesecurity/wordpress-security-top-tips-secure-wordpress-application/.

上記は、Acunetix というウェブベースの脆弱性スキャナを開発している会社が公開したページです。2013年には WordPress で動作している 90,000 のウェブサイトが乗っ取られてしまい、ボットやマルウェアの踏み台あるいは拡散源として悪用されたといいます。このため、WordPress で運用しているサイトのセキュリティを高めると、いたずらに他人のサイトを乗っ取ろうとする人々や犯罪者へ重いインパクトを与えることになるでしょう。

Acunetix の記事で推奨されている対策は、以下のとおりです。

以上のうち、強調した項目は本稿で取り上げていない対策であり、また参照するだけの価値があると思います。他にも本稿で取り上げていない項目はありますが、たとえば「デバッグ用のログを保護すること」という対策は、WordPress の実装だけに限った話ではないので割愛します。テスト用のコードやファイルを保護するとか、テスト用のコードやファイルをプロダクションサーバ(公開用のウェブサーバ)に放置しないというのは、少なくともプロの制作プロダクションでは常識にあたります(IR の資料や個人情報が含まれるファイルなど、放置したファイルの種類によっては、発注元クライアントから損害賠償を請求されることもあるからです)。もちろん、本稿ではプロの制作者を想定しているわけではありませんが、オンラインにウェブサイトを公開する際に、それを制作したり実装したのがプロなのか素人なのかは、ページを閲覧するビジターにとってはどうでもよいことです。素人にもそれなりの責任があるということは弁える必要があるでしょう。たとえそれが気の毒な境遇の人々を救うとか何らかの社会的なメッセージを発するといった善意から公開されていようと、サイトに訪問した人たちを危険にさらしたときの言い訳にはなりません。自信がないのであれば、オリジナルのドメインだとか、自分で作ったデザインなどという、実はどうでもよい本人の自己満足にすぎない些事にこだわるのをやめて(これはプロの制作者としても言っています)、Facebook や、同じ WordPress で動く wordpress.com を利用しましょう。

さて、参考になる新しい対策の中で、特に説明しておきたいのは「パーマリンクの機能で起きるユーザ名の漏洩を防ぐ」という対策です。WordPress の管理画面へログインするには、認証情報(credentials)という情報を使います。これはもちろんユーザ名とパスワードのことですが、思慮の足りない開発者や情報セキュリティの専門家・技術者を自称する人の中には、パスワードを保護することだけしか頭にない人もいます。しかし、ログインするときに両方の情報を入力する必要があるという事実で分かるように、ユーザ名も重要な認証情報の一つであって、適切に保護しなくてはなりません。特に、コーポレートサイトを WordPress で運用する企業などではページのコンテンツを記事や WordPress Pages として作成しますが、特にページ内にユーザ名を表示したりはしないでしょう。仮にそれが "admin" であれば認証情報の一つを公開してしまうことになりますし、"Kawamoto" のように運営担当者の名前であれば不必要に識別情報(個人情報を構成しうる一部の情報)を意味もなく公表してしまうことになるからです。

しかし、Acunetix の記事では別のページで詳しく解説されているように、WordPress で「パーマリンク(Permalink)」という機能を有効にしていると、ユーザ名が漏洩してしまう可能性があります(ただし、公開状態の記事を一つでも書いていることが条件)。パーマリンクを有効にしていないディフォールトの状態では、特定の記事や WordPress Pages へアクセスする URL は http://*****/?p=91 のようになっていて、これは「クエリベースの URL」と呼ばれます。パーマリンクを有効にすると、/?p=91 という箇所を WordPress とウェブサーバの機能で /archives/91 としてもアクセスできるようになります。したがって、http://*****/?p=91 と入力しなければいけなかったページに http://*****/archives/91 という URL でもアクセスできるため、いっときは SEO 業者などが「検索エンジンにやさしい URL」などと宣伝していました(現在は、クエリベースであろうとなかろうと有利・不利はほぼありません。当たり前のことですが、要するにコンテンツの価値がポイントなのです)。しかし、このパーマリンクを有効にすると、http://*****/?author=1 という URL で、ユーザ ID が 1 であるユーザ(仮にそのユーザのユーザ名を "philsci" とします)の公開した記事の一覧にアクセスできてしまうのです。そして、この URL にアクセスすると http://*****/author/philsci という URL に書き換えられてしまいます。このため、攻撃者が http://*****/?author=1, http://*****/?author=2, http://*****/?author=3 と順番に試していけば、そのサイトのユーザとして登録されているユーザ名が全てばれてしまう可能性があります(このリスクが「ユーザ名の列挙(username enumeration)」と呼ばれている理由は、ここにあります)。というわけで、このリスクを避けるには "/?author=" というクエリベースの URL でアクセスできないようにウェブサーバを設定する必要があります(もちろん、パーマリンクを無効にすれば避けられるリスクですが、パーマリンクの機能自体は使いたい方も多いでしょう)。ただし、この対策をすると「ユーザごとの記事一覧」は表示できなくなるため、パーマリンクなしでユーザごとの記事一覧を表示したい(でもユーザ名は出力したくない)場合は、自力で PHP のコードを書く必要があるでしょう。

2015-06-05 の追記

次に、前篇では管理画面のコメント一覧についてリスクを指摘しましたが、2015年6月現在のバージョン 4.2.2 では、コメント一覧を表示しても JavaScript は実行されないようになっています。しかし、このコメント一覧では、下記の画像のようにコメントの文字列が正しく表示されません。

hidden literals in comment

実際には、このコメントは <script>alert('xss is fun');</script> という文字列になっていて、「投稿を表示」をクリックすれば JavaScript のポップアップ表示が出てきます。ということは、"alert('xss is fun');" という文字列が JavaScript のコードには見えないように記述されていれば、このコメントを承認してしまうと、サイト上で記事を読む人に JavaScript を実行させてしまうリスクがあります。例えば、下の画像で示したような誰かの署名みたいに書かれたコメントでは、JavaScript のコードがコメントに含まれていることがすぐに分かるでしょうか。

dubious comment

2015-09-17 の追記

WordPressの運用上のセキュリティについて考えよう」という記事は、WordPress 本体だけでなく、WordPress を利用する環境のことも考慮した内容になっています。WordPress のセキュリティに関する圧倒的多数のブログ記事が、他人の記事からのコピペか、海外ブログからの(無断翻訳も含む)翻案にすぎないのと比べて、この記事には参考になる点が幾つかあり、一読の価値はあると思います。しかし、「おまけ」として掲載されている、既存の対策を論評した箇所には、残念ながら全く同意できない内容があります。

WordPress の username は漏洩しない方がよいとされていて、初期設定の "admin" はもとより、author のアーカイブなどで URI に username が使われてしまうのはよろしくないと思います。これは上記の本文でも紹介したとおりです。これについて、著者である「め組」(デジタルキューブ)の gatespace さんは、

何をもって危険と言っているのでしょうか?

ユーザー名が分かったところで パスワードが破られなければダッシュボードにログインすることは出来ません。

WordPress に限りませんが、他のウェブサービスとパスワードを同じものにしたり、桁数が少ない、容易に推測できるパスワードであればユーザー名が何であれ危険な事には変わりません。

と書いています。もちろん、企業の情報セキュリティマネジメントの責任担当役職者(そして情報セキュリティに関わる専任技術者)の観点から言って、ユーザ名を無用に公開することは、credentials の一部を漏洩していることになるので危険だと考えます。

上記のような意見は、情報セキュリティの指標である CIA(機密性、完全性、可用性)という観点に照らすと、或るリソースにアクセスするにあたって守られるべき機密性(confidentiality)として username とパスワードが一式で credentials を構成しているのだという理解が不足しているように思います。もし仮にパスワードだけを強くして保護すればいいなら、もともとログイン情報の管理・運用は各所で面倒だと言われているわけなので、いっそログイン画面から username を入力する欄など取り外して、パスワードだけ入力して認証すればよいのではないでしょうか。(他のユーザとパスワードが重複すると identity が混乱して認可に信頼性がなくなるため、サイト全域、つまりシステム管理側のユーザも含めてパスワードの重複を認めなければよいでしょう。また、パスワードをユーザに任意で設定させると、重複したときに「このパスワードは既に使われています」などとエラーが出たら他人のパスワードが分かってしまうため、パスワードは管理システム側から自動で発行するのみとすればよいでしょう。実際、そういうシステムを僕は会社の「情報セキュリティ定期テスト」用に構築して運用しています。ただし、パスワードは情報セキュリティ事務局だけが設定し、32桁の文字列にしています。とは言え、平文でデータベースの値を取得できなくなれば、ユーザごとに salt を設定しても取得できなくなるため、これはこれでセキュリティ上の欠点にはなります。)

僕はふだんから、「username もパスワードと同様に保護すべき認証情報の一部である」という方針で自社のセキュリティもマネジメントしています。したがって、たとえば FTP アカウントに従業員の名前を "[root@hostname ~]# useradd kawamoto" のように設定する運用は間違いだと考えていて、それゆえ FTP アカウントの username にも 16 桁のランダムな英数文字を設定しています。このような文字列は、どれほど複雑であろうとソフトウェアに設定すれば何度も入力する必要はありませんし、パスワード管理ツールを使えば幾らでも運用できます。

パスワードさえ破られなければいいという考え方は、メールアドレスのような、公開しうる(したがってハッシュ化してデータベースに格納できない)情報を username に流用していた大昔の杜撰なソフトウェア設計の悪習を引きずっているだけにすぎません。したがって、たとえば Twitter のアカウントがハックされやすい一つの理由は、username をそのまま不特定多数に向けて表示したり、URI の一部として設定するなどの杜撰な設計に理由があると思っています。

"Privacy by Design" と共に言われることもある "Security by Design" に沿った適正な考え方の設計では、表示用の名前と、credentials の一部である username とを最初からどちらもサポートして、username はユーザ登録時に最初からハッシュ化してデータベース保存し、認証ではパスワードと同様に username もハッシュを比較します。もし表示用の名前を設定しない段階で管理画面上に username を「~さん、いらっしゃい」のように表示したいなら、フォームから username 文字列を受け取った段階で一時的に保存し、認証が成功した後に暫定で引き回せばいいだけです(もしユーザが表示用の名前を設定したら、そんな必要もありません。一部の CMS で username を変更できなくしてあるのは、そういう仕組みがあるからだと思います)。

gatespace さんの文章を読む限り、正しくマルチレイヤーの考え方を取り入れているような箇所もありますが、どうしてマルチレイヤーでコンテンツや認証情報を保護することが望ましいのかを理解していないと、単純に「これこれを対策するのだから他の対策は必要ない」という中途半端な文章になってしまいます。もちろん、WordPress のセキュリティについて書かれている記事には、論外と言ってもいいようなことを書いているものもあります(WordPress を使っている人の多くがセキュリティどころかプログラミングの素人であるにもかかわらず、SEO なり他の事情などで生半可なことでも語りたいという動機をもっている場合が多々あります。WWW において、そのような情報発信を止めることはできませんし、止めるべきでもありませんが、端的に言ってそのような素人の情報発信がノイズであることに変わりはありません)。したがって、そのような「俗説」があれば批評して、誤解が蔓延しないようにするのは正しいことだと思います。しかし、一種の理想としてではありますが、マルチレイヤー防御の基礎になっている「情報セキュリティ上の対策は、可能であれば全て採用するのが望ましい」という原則を心得ていれば、一部の対策をやれば他は必要ないという考え方は、マルチレイヤーの考え方に反するものであり、逆に言えば WordPress の情報セキュリティ対策には特効薬があると言っているのと同じであることに気づくはずです。

冒頭に戻る


※ 以下の SNS 共有ボタンは JavaScript を使っておらず、ボタンを押すまでは SNS サイトと全く通信しません。

Google+ Twitter Facebook