WordPress のセキュリティ対策

河本孝之(Takayuki Kawamoto)

Contact: takayuki.kawamoto@markupdancing.net

ORCID iD iconORCID, Google Scholar, PhilPapers.

First appeared: 2008-11-19 22:55:00,
Modified: 2015-09-16 16:00,2015-10-07 12:00:00,2017-03-03 17:18:47,2019-02-19 17:39:35,2019-04-11 17:08:18,2023-01-03 11:51:15,
Last modified: 2023-01-05 11:15:54.

本稿は、2008年から2019年にかけて10年ほどの間に書き足したり更新してきた、WordPress のセキュリティ対策に関する5本ていどの記事をまとめた論説です。このたび再掲載するに当たって、プロの実務家として当たりまえのことですが、間違った内容だとか、現在では有効とされていない対策については訂正したり削除して掲載しますので、それぞれの論説を当時のまま再掲載しているわけではありません。また、本来であれば今後も(WordPress がリリースされている限り)見直しや更新の必要はある筈です。実際、僕も仕事では使い続けていますから、気づいたことは追記したり修正してアップデートしていく予定です。

第1部(前編 [基礎編])

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

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

本稿では 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 のセキュリティにかかわる話題も出てくると思うので、参照されるとよいでしょう。

追記 (2023-01-03)

上記のように書きましたが、やはり WordPress のセキュリティ対策は十分に議論されておらず、また WordPress の解説本などにも十分な説明がないまま広く普及してしまっている感があります。いまではレンタル・サーバでボタン一つでインストールできてしまうため、ウェブの制作会社で受託案件で WordPress をレンタル・サーバに実装するときですら、同じような感覚で作業している人が多いようです。そういうロボット同然の自称プログラマばかり増えていっても、結局は「デジタル・ネイティブ」と呼ばれるブルーカラーや消費者ばかり増えてゆき、リテラシーの底上げにはならないでしょう。ということで、情報セキュリティのハッカーではありませんが、電通・博報堂案件で WordPress の実装では50件近くの実績がある僕のような人材でも、本稿で貢献できる余地はあると考えます。寧ろ、本稿を凌駕する論説を若手の方々に書いてもらい、「ばーか。老害は早く死ね」とか言ってもらうくらいでちょうどいいのではないかと思っています。ていうか、そうでなきゃ、いつまでも俺の方が「ばーか。都内の IT ブルーカラーはド田舎でパン屋でもやってろ」とか自分が死ぬまで言い続けるのって、日本人として恥ずかしいよ。

まずどのような方を対象とするかですが、全ての 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>TEST</title>
	</head>
	<body>
		<h1>TEST</h1>
		<p>これはテストです。</p>
	</body>
</html>

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

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

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

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

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

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

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

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

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

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

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

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

[追記:2023-01-03] 本稿では他の個所でも WordPress の CGM ドキュメントであった Codex を参照していましたが、2023年の現在は Codes の運用が英語版も日本語版も停止されているため、本稿では割愛します。

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

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

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

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

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

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

それから、僕自身が利用していなかった機能として、メールを使った投稿機能があります。当サイトの記事は長文が多く、それなりにまとまった分量の記事でなければ公開しないと決めているので、出先からメールで数行の記事を投稿する場合は Posterous に投稿するという使い分けをしていました(もちろん、2015年の現在は殆ど Google+ に書いているので、この箇所は 2008 年当時の話です [追記:2023-01-03] そして2023年までに 一般サービスとしての Google+ や、それからビジネス版の Google Workaspace で提供されていた Google Current という後継サービスも含めてサービスが終了したので、現在はここで書いているようなことはやっていません)。すると、メールでサーバに記事を送り、WordPress から POP3 プロトコルでメールを読みに行くという機能は不要です。

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

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

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

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

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

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

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

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

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

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

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

WordPress

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

WordPress

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

WordPress

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

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

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

繰り返しますが、上記の内容は、2008年当時の WordPress について説明しています。2023年現在の WordPress では、コメントの管理ページに一覧表示のスタイルを切り替える機能はありません。そして、困ったことに管理ページでは <script> のような文字列はエスケープして表示されてしまうのに、ソースではエスケープされないため、管理ページの一覧ではタグに見えないからといって許可してしまうと、実際にブログで表示されるコメントはエスケープされていないまま扱われて、スクリプトが動いてしまいます。試しに、ローカル環境で WordPress を動かしているなら、コメントに JavaScript の実行コードを書いてみてください。管理画面では動きませんが、それを許可してしまうと公開する側では JavaScript が動いてしまいます。

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

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

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

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

WordPress

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

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

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

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

冒頭に戻る

第1部(後篇)

適用範囲について

さて、後篇では前篇と同じく共有のレンタル・サーバへインストールするというケースを想定します。つまり、レンタル・サーバ会社がシステムを用意していて、インストールもアップデートもレンタル・サーバ側の代行システムを使って行うような、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: CODE
[...]
68  2008-11-13 18:37:26.395537  ****.jp  192.168.1.59  FTP  Response: 226 Transfer complete.

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

これでお分かりのように、多くの方は何気なく FTP ソフトでサーバにファイルをアップロードしているわけですが、この行為は情報セキュリティの管理者やシステム開発を仕事にしている人たちから見ると、自然なことでもありませんし、ましてや安全な行為ではありません。少なくとも情報セキュリティの管理体制について ISMS(JIS Q 27001)のような認証を受けている企業では、メールに 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_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, 1, '2000-01-01 00:00:00', '0000-00-00 00:00:00', '', 'TEST', '', 'auto-draft', 'open', 'open', '', '', '', '', '2000-01-01 00:00:00', '0000-00-00 00:00:00', '', 0, 'https://foobar.gom/', 0, 'post', '', 0);

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

public $tables = array(
	'posts',
	'comments',
	'links',
	'options',
	'postmeta',
	'terms',
	'term_taxonomy',
	'term_relationships',
	'termmeta',
	'commentmeta',
);

したがって、“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 以降ではキーコードを wp-config.php で設定するようになっています。

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' );

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

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 さんのサイト [2023年の時点で再確認するとサイトが無くなっていたので、Internet Archive のアーカイブにリンクします。本稿では他にも同じ処置を施している事例があります] で紹介されていますので、興味がある方はご参照ください。僕の場合は、パスワード管理ソフトに付属しているパスワード生成機能を使って、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 ジェネレータ [2023年1月現在の最新版] で出力してみるとよいでしょう。

対策(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 の管理ユーザがどのような条件で管理画面にアクセスしているのかを特定しておく必要があります。或る決まったマシンやネットワークセグメントからだけアクセスするなら、例えば自宅で使っているマシンや会社で使っているマシンからだけ管理画面にアクセスするというケースに限定して、アクセスを幾つかのやり方で制限できます。

具体的には、ウェブ・サーバが Apache だと AllowOverride が有効になっている前提で、.htaccess に IP アドレスを指定して /wp-admin へのアクセス元を制限します(俗に「IP 制限」と言われます)。例えば、

# まだ使えるが古い書き方
Order deny,allow
Deny from all
Allow from 202.***.***.***
Allow from 168.***.***.***

# Apache 2.4.x での正式な書き方
Require all denied
<RequireAny>
	Require local
	Require ip 202.***.***.***
	Require ip 168.***.***.***
</RequireAny>

のように記入した .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 でアクセスを拒否してしまうと、ページが正しく表示されません。そこで、.htaccess を使って全域に制限をかけてから、例外のルールを追加します。

# まだ使えるが古い書き方
Order allow,deny
Deny from all
<FilesMatch "\.(css|jpeg|jpg|png|gif|js|swf)$">
Allow from all
</FilesMatch>

# Apache 2.4.x での正式な書き方
Require all denied
<FilesMatch "\.(css|jpeg|jpg|svg|png|gif|js)$">
	Require all granted
	Require local
	Require ip 202.***.***.***
	Require ip 168.***.***.***
</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 サーバ証明書を使った HTTPS 通信を使うことが望ましいと言えます*

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

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

対策(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)」を設定して、オリジナルのパターンで権限をもったユーザをつくってブログを運用したい場合は、下記のように細かく権限を設定できるプラグインも公開されています。

WordPress

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

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

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

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

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

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

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

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

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

対策(16): その他

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

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

directory index

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

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

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

# まだ使えるが古い書き方
<FilesMatch "^wp-config\.php$">
	deny from all
</FilesMatch>

# Apache 2.4.x での正式な書き方
<FilesMatch "^wp-config\.php$">
	Require all denied
</FilesMatch>

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

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

最後に

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

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

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

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

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

追加レビュー

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

Acunetix, “Top tips to prevent a WordPress hack” retrieved in 2015.

上記は、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 は https://*****/?p=91 のようになっていて、これは「クエリベースの URL」と呼ばれます。パーマリンクを有効にすると、/?p=91 という箇所を WordPress とウェブ・サーバの機能で /archives/91 としてもアクセスできるようになります。したがって、https://*****/?p=91 と入力しなければいけなかったページに https://*****/archives/91 という URL でもアクセスできるため、いっときは SEO 業者などが「検索エンジンにやさしい URL」などと宣伝していました(現在は、クエリベースであろうとなかろうと有利・不利はほぼありません。当たり前のことですが、要するにコンテンツの価値がポイントなのです)。しかし、このパーマリンクを有効にすると、https://*****/?author=1 という URL で、ユーザ ID が 1 であるユーザ(仮にそのユーザのユーザ名を “philsci” とします)の公開した記事の一覧にアクセスできてしまうのです。そして、この URL にアクセスすると https://*****/author/philsci という URL に書き換えられてしまいます。このため、攻撃者が https://*****/?author=1, https://*****/?author=2, https://*****/?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 が使われてしまうのはよろしくないと思います。これは先に Acunetix の記事で紹介したとおりです。これについて、著者である「め組」(デジタルキューブ)の 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 の情報セキュリティ対策には特効薬がある」と言ってしまっているのと同じであることに気づくはずです。

冒頭に戻る

第2部(補遺1)

本稿は、2016年の2月に更新を終了した「WordPress のセキュリティ対策」という議論(「先の第1部」と呼びます)の補足です。僕は先の第1部の更新を終了した際に、少なくとも日本で活動されている多くのセキュリティの専門家が WordPress の運用について発言したり著作を出すようになったため、知見も責任もある彼らに任せたほうが良いと判断したと書きました。

しかしながら、そうした著名な方々でも十分満足に情報を公開したり更新しているわけではありません。他方で、少なくとも関西のウェブ制作業界においては、僕も非機能要件を適正に満たすべき成果物の品質を管理しナショナル・クライアントや大手広告代理店へ責任を負う立場にある者として、自分のサイトでメンテナンスを終えた不十分なセキュリティ対策の文書を(いくら「古い」と断り、記録としての意味しかないと言い訳を付け加えているからといって)公開し放置し続けるのは無責任というものではないかと考え直しました。つまり、それなりに狭い(あるいはレベルの低い)意味合いでは、もちろん僕自身もセキュリティの実務家であり業務上の責任を負っているプロフェッショナルの一人なので、コンテンツを公開し続けるのであれば一定の責任に応じて内容をメンテナンスした方がよいのでしょう。ということで、必要に応じて本稿でコンテンツを追加したり、必要に応じて先の第1部を修正していきます*1

本稿では、追加したい話題を章立てで区切って公開するため、前後の内容に時間上の、あるいは推論上の関係があるとは限りません。つまり冒頭のコンテンツを読まないと後のコンテンツが理解できないという書き方はしていませんし、逆の順番についても同じです。但し、それぞれの文章で仮定している経験や知識は異なるので、一応の目安として、[G] = General topics, [B] = Business use topics という区別はしておきます。[G] は先の第1部と同じていどの知識があれば理解できると思いますが、[B] は情報セキュリティの実務なり業務として納品する立場にある人でなければ理解できないことが多いと思います(とは言え、それは「高級な話題だから」だとは限りません。そういう業務上の用途でもない限りは、多くの人は特に考えなくてもいい話題かもしれません)。

なお、このような文書は、本来は僕が情報セキュリティ系の別サイトとして運営している identifiable.info [2018-02-25の補足: 既に閉鎖したサイトです。コンテンツは全て当サイトへ移してあります] で公開するべきかもしれませんが、正確に言うとこのサイトはドメイン名が表しているように online identity や個人の認証をテーマにしたいという意図で始めたので、情報セキュリティ全般の話題を混在させると運営方針が不明確になってしまうため、このまま当サイトで公開します。

*1新しく記事を公開する理由は他にもあります。やはり依然として(僕らの観点から見ると)アマチュア、あるいは生半可にセキュリティの考え方を聞きかじった技術者が不適切な対策を気軽にブログ記事として公表しているようなので、こうした間違いを指摘する嫌われ役が必要なのでしょう。もちろんそのような「掃除」を WordPress のセキュリティに関する著名人たちに期待してもいいわけですが、得てして有名となった方々は、素人の間違いをあからさまに指摘するといった野暮なことはしなくなって「いいひと」になりがちです(また、素人の書いたものをいちいち訂正すると「無料の家庭教師」や「無償奉仕のセミナー講師」になってしまうからでもあります)。このため、皮肉にも WordPress のセキュリティについてエスタブリッシュメントとされる人々が明確な発言を避けるようになり、皮肉にも間違いが間違いとして放置されてしまいます(更には、間違った対策を主張しながら「エスタブリッシュメント」となってしまった立場上、簡単には訂正できなくなってしまうという問題もあり得ます)。

日曜プログラマが自分の使っている WordPress サイトで勝手に脆弱な対策を施し、知らない間にロシアのスケベ姉ちゃんの写真をトップページに掲載されて家庭不和となるくらいの些事なら、全世界のブログで頻発しても大して驚くようなことではありませんが、僕らが業務で預かっているクライアントのブランド価値や個人情報の価値は、規模に関係なく制作プロダクション企業が簡単に倒産するようなインパクトとなり得ます(もちろん、個人のトラブルの方が瑣末だからというだけではなく、専門家の個人情報から愚かなセキュリティ対策を導入している人たち自身の個人情報も含めて影響の範囲が広いからです)。僕は納品したウェブサイトで使っている CMS やフレームワークや個々のコードや OS を全てメンテナンスしたり監査しているわけではないので(というか、そんなことは他社でも IT ゼネコンでも不可能でしょう)、当社の人間やクライアントがオンラインで公開されている間違った対策を勝手に導入しても気づきませんし、そもそもオープンソースのツールであろうと有償販売のシステムであろうと、使っている側に無条件で全ての責任があるなどという契約は民事として成立しません。とは言え、事前に愚かな対策を導入しないように牽制する責務はあると思うので、このような文書を作成して一定の範囲でメンテナンスしようと決めました。現に、僕が取引先でレクチャーしたり社内研修を開催するときには、先の第1部も含めて(もちろん当サイトはプライベートな目的で運営しているので、この URL を紹介しているわけではありませんが)、ここで書いていることを具体的に紹介しています。従って、本稿は、個々の実装では与件に応じて色々な対処の仕方があるとは言え、ポリシーとしては当社の実装レベルはこの記事と同じであると言えるていどに僕自身の現実の職責と関連している文書です。

[G] バージョン表記の抑制について

WordPress の「セキュリティ対策」として頻繁に語られるのが、バージョン表記の抑制です。これは Apache のような他のアプリケーションについても、ServerSignature ディレクティブでエラーページのバージョン表記を抑制するべきかどうかが話題となってきました。

具体的に言うと、WordPress が出力しているページのソースをブラウザで表示すると [2023年1月時点の最新版]、

<meta name="generator" content="WordPress 6.1.1">

のようにバージョン番号が <meta> 要素として自動で出力されます。これを情報漏洩の一種だとでも考える人々がいて、「セキュリティ対策」と称しては抑制する方法を SNS で公言したりオンラインでページを公開したり本や雑誌に書いているわけです。

しかし現在のユースケース、技術的な可能性、それから現実に起きているインシデントで使われている手法がどれくらい簡単に入手して使えるかを考慮すると、バージョン表記を抑制するていどのことでリスクが実質的に低減した時代は既に終わっていると考えるのが妥当です。その最も大きな根拠は、WordPress で構築されたウェブサイトに対する攻撃の多くは大量のボットで構成されたボットネットで自動化された、無差別かつ大規模な手法を使っているという事実にあります。いまや、攻撃する側は人間ではなくプログラムなのですから、WordPress のバージョンが幾つなのかとか、それがページに表示されるかどうかなどということは気にしません。つまり、バージョンがいくつであれ、いまそこで動いている WordPress が脆弱性について対策できていなければいけないという、ごく当たり前の事実だけが残るわけです(最初は「最新でなければならない」と書いていましたが、もちろん最新のバージョンにしているからといって安全だとは限りません。せいぜい「既知の脆弱性について一定のセキュリティ・レベルの対策を施した」と言えるだけです)。バージョンの表示を抑制するなどというハッタリがセキュリティ対策になるなどという牧歌的な時代は終わったのです。

セキュリティの原則として考えても、情報を秘匿するという手法は一般的に「下策」とされています。なぜなら、情報を隠蔽する方法も隠蔽できなければ万全とは言えず、そのまた情報を隠蔽する方法を隠蔽する方法も隠蔽できなければならず・・・と切りが無くなり、つまりは万全な隠蔽方法などないからです。しかし、上記のようなメタ情報の隠蔽がそもそも隠蔽として適切であるかどうかを考えると、そのサイトを攻撃してみるだけで WordPress が古いかどうか判定できるのですから、実質としても隠蔽の役にすら立っていないことは明らかです。そもそも、攻撃する側が目指しているのは WordPress のバージョンを知ることではなく、情報を盗んだりサイトにマルウェアや不正ページへのリンク等を仕込むことなのですから、つまるところバージョン番号などどうでもよいわけです*2

*2上記に加えて、例えば WordPress の公式ドキュメント群と言える WordPress Codex というサイトで情報セキュリティ対策を解説しているページをご覧いただいても、バージョン情報を抑制せよなどということは一言も書かれていません。[2023年現在、Codex は公式のドキュメントではなくなりました。]

僕がこの手の「セキュリティ対策」とやらを見かけて最初に感じたことは、WordPress のバージョン番号を秘匿して「古いバージョンでサイトを運用していることが分からないようにする」のが情報セキュリティだと説明されていることへの不信感でした。上記でも述べたとおり、脆弱なままのシステムを放置して、そのバージョン番号の表示を抑制すれば安全になるという牧歌的な発想がセキュリティの原則としても幼稚であるという点はもちろんですが、それはつまり WordPress のバージョンを隠すというよりも寧ろ、システムの改善が何らかの事情でできないという運営側の失態や、予算不足というファイナンス上の失敗(僕は企業の役職者でもあるため会社経営の立場からも書きますが、予算を確保できないのは組織の人員なり事業に問題があるからであって、運のせいでもなければ不況や世の中のせいでもありません)、そして会社に資金があっても情報セキュリティへ(もちろん過剰ではなく適度に)投資する経営判断が欠けているという実情を隠蔽しているだけではないかと思えるからなのです。そういう会社には、そもそも WordPress 以前にウェブサイトを運営する資格などありません。Facebook にでも会社の情報を無料で掲載しておけばいいのです。

社内に、この程度の「セキュリティ対策」とやらを検索して見つけてくるくらいの従業員はいても、WordPress をアップデートすると現行サイトにどういう影響があるかを予め検証できる従業員がいないのであれば、それができる外部スタッフやセキュリティ企業に相談するという発想がすぐに出てこなければなりません。情報セキュリティの初心者にとって最も大切な基本は、自分達に基礎的な見識が不足していると自覚することなのです。僕も含めて、古今東西の誰であれ人は完全無欠ではありえない以上、色々な分野について平凡どころか基本的な素養すら身に着けていないわけです。情報セキュリティのプロフェッショナルとして活動している人々の全てが個展を開催できるくらいの書道の才能があるわけでもなく、ハーヴァード大学の医学部教授を全員集めて一つのテーマに没頭させても10年以内に毛生え薬や癌のワクチンが開発されるという期待は殆どできないでしょう。つまり、専門であろうとなかろうと、どのような人にとっても困難なことが数多くあり、我々はそういうものと関わりながら生活したり仕事をしているという、或る種のリスクにさらされています。そして、ほぼ全ての人は、そういうリスクが実際に顕在化して対策を講じなければならなくなったときに、自分自身が適正な情報とスキルを弁えていて対策を講じられるとは限らないと自覚していなければなりません。この自覚がない人は、会社経営であろうとウェブサイトの運営であろうと、あるいは家庭を持つということでも構いませんが、そういう「マネジメント」が必要とされる事案へ軽率に関与すると大多数は失敗を引き起こしますし、それによって被害を蒙るのが他人でもあるならなおさら、どのように関わるべきなのか、あるいは関与するべきかどうかについて慎重であることを求められます。

[B] 管理画面としての WordPress

成果物として WordPress で構築したウェブサイトを納品する場合、セキュリティ対策の一つとして僕が(もちろん闇雲・一律にではなく必要に応じて)採用しているのは、WordPress を管理画面としてだけ利用するという手法です。これはつまり、ウェブサイトで記事やページを作成・更新するのがトップ・クライアントの担当者であり、CMS を導入する目的の一つが彼ら担当者の負荷を軽減することにあるなら、WordPress のような CMS を導入するという与件を導き出している「要件」はサイトの運営業務の負荷を軽減したいということであって、「要求」は彼ら担当者がオンライン業務の専任者でもなければウェブコンテンツの制作や運用についてスキルをもつインセンティブが存在しないのでシステムに任せたいということになります*3。もちろん、これらの要件や要求を汲み取っている我々は、殆どの場合にはそれら要件や要求について「お小言」を言う立場にはありません。マーケティングがどうのウェブ戦略がどうのと瑣末なビジネス用語をまくし立てようと、結局はおまえら面倒臭いだけだろうと見越した上でも、表面的には彼らの要求を満たす要件を設定して仕様をまとめるのが受託サービスというものです(もちろん限度はありますが)。通常、システム開発を受託する企業において、実装したり開発するシステムつまり成果物だけでなくサービスの設計やビジネス・メソッドにも関わるような「アーキテクト」と呼ばれる職種の人々は、単にプログラムやサーバの仕様だけではなく営業担当のディレクターに話を聴いたりクライアントへ直に接触して、彼らが実際に何を求めているのかを「要件」や「要求」として掴み取る必要があります(僕は社内で正式な職位として「アーキテクト」を拝命しているわけではありませんが、職能としてはアーキテクトとして振る舞うことを期待されている立場でもあります)。

ということで、WordPress を利用したウェブサイトの構築を担ってきた経緯から、WordPress を管理画面として導入するという手法を採用することが何度かありました。トップ・クライアントの多くは(広告代理店や当社のディレクターに、自分たちの要求とは関係なく勧められたからという理由とは違って)、自分たちで(もちろん自分たちの)ウェブサイトを一定の範囲でコントロールしたいと望んでいます。しかし、HTML のコーディングや部品画像のデザインをしたいという(いわば多くの人にとっては趣味的な)欲求まではありません。こういう場合、ウェブサイトの運営を楽にするために CMS を導入するのは自然な提案ですが、それはあくまでも彼らトップ・クライアントの担当者の事情であって、コンテンツを利用するビジターの事情は関係がありません。そして、動的なしくみである CMS を導入するということは、ウェブサイトだけでなくビジターをも一定の脅威に晒すこととなるため(ウェブサイトがワームなどに感染すると、ビジターにも脅威となるのは当然です)、先の文書でも、そもそも CMS を導入する必要があるのかどうか検討しようと書きましたが、CMS を導入するべき理屈を自分たち運営側の事情だけで組み立ててはいけないのです。アーキテクト、とりわけ情報セキュリティにも関わる人間は、情報資産の適正な運用を全てのステークホルダへの影響について評価しなくてはいけませんから、ウェブサイトの運用・運営にかかわるステークホルダでもあるビジターへの影響は無視できません。

このような理由から僕の選択した手法が、WordPress を管理画面としてだけ構築し、フロントエンド側のコンテンツはオリジナルのコードで構築するということでした。模式図は下記のようになります。

WordPress as an administration system

*3もちろん、それに加えて、単に「話題の CMS を使ってみたい」という理由で WordPress を与件に引っ張り出す人がいないわけではありません。また、CMS を使えば構築・制作の工数が削減されて費用を抑えられると考えているのかもしれません。実際、受託側のディレクターも「制作費用を削減できる」とセールスする場合もありますが、その場合は CMS を使えば CMS の仕様を理由に後から余計な機能を追加しろと言われても断れるとか、CMS の仕様を拡張するような追加要求には特別な費用を請求できるといった、営業担当者としての目論見があるはずで、単に顧客の予算を低く抑えるためだけに CMS を提案するようなディレクターは、人物としては誠実かもしれませんが、企業人としてはナイーブすぎると言えます。もちろん顧客の予算を不当に引き上げろと言いたいわけではありませんが、後から発生しうるリスクを予測して先手を打つことは別に何も悪逆非道なことではないでしょう。

WordPress は管理画面としてだけ運用するので、WordPress は MySQL 互換の DBMS とファイル・システムに対して正常に動作すれば十分です。テーマは必要最低限(というか実質としては不要)でよいため、ディフォールトのテーマを選択し、それ以外のテーマは全てサーバから削除します。ウェブサイトのセキュリティを維持する些細でありながらも確実なコツの一つは、「使わないファイルを放置しない」(少なくともウェブ・サーバの公開領域に放置しない)ということです。残念ながら、WordPress の更新機能を使っている場合には、自動更新であろうと管理画面からの手動更新であろうと、アップデートのたびに削除したファイルが勝手にサーバ内へ再び展開されてしまいます。殆どのコーポレート・サイトではコメント機能やユーザ登録機能などは不要ですし攻撃場所を放置するだけなので、/wp-comments-post.php や /wp-signup.php などはサーバから削除するのが安全ですが、WordPress を更新すると再びサーバに設置されてしまいます。WordPress の自動更新という「なんちゃって自動化」(実はビジターのアクセスをトリガーにしているだけなので、アクセスのないサイトは全く更新されない)ではなく、PHP と crontab で本物の自動処理を追加できる場合には、逆に不要なファイルをサーバから削除するスクリプトを書いて1時間ごとに動作させてもよいわけですが、そういうスキルも準備もしていない場合は、知らない間に削除したはずのファイルが再びサーバ内へ展開されていることに気づかないままとなるかもしれません(特にトップ・クライアント企業の運営担当者は FTP ソフトでサーバにアクセスする理由も責任もない・・・と思っていることが多いため、納品された後は管理画面にしか関心が向かないものです)。

こういう状況に対応する一つの手法が、WordPress は導入するがフロント・エンドは独立させるということでした。この手法を導入する場合、ウェブサイトの FQDN を www.test.com とすると、admin.test.com のようなサブドメインを「管理サイト」として WordPress をインストールし、admin.test.com へはサイト運営者が利用しているネットワークの IP アドレスからのアクセスだけを許可したり、ベーシック認証を設定するなどの処置が望ましいです。こうして管理サイトを独立させると、公開サイトには WordPress に関わるファイルが一つもなくなるため、フロント・エンド側の作業は非常に見通しがよくなります。

WordPress で運用する管理サイトと公開サイトを分離した後は、公開サイトの構築は一般的な PHP なりウェブ・アプリケーションのセキュリティ対策を守って構築すれば問題ありません。WordPress のデータを利用するため、特に SQL コマンドの扱いと、ウェブページを出力するときのデータの扱い、それから HTML や CSS や JavaScript などで扱う入力値や出力値といった動的要素の処理に気をつければ、あとは Apache や IIS のようなウェブサーバ、PHP の処理系、それ以外の通信系のコマンドや OS の動作に関わる機能に問題がない限りは、対策としてはひとまず一定のセキュリティ・レベルを維持できるでしょう。更に、ウェブ・ページへアクセスするたびに DBMS サーバへアクセスするのはサイトが重くなるので嫌だという場合には、コーポレート・サイトの場合ならコンテンツが頻繁に書き換わることはありませんから、<body> 内の要素を予めテンプレートと組み合わせてテキスト・ファイルとして出力しておき、ウェブ・ページへのリクエストに応じてコンテンツを読み込むという仕組みを導入してもよいでしょう。この場合は、管理サイトで記事を作成して publish したり update するたびにコンテンツ用のファイルを上書きで出力するような機能を組み込むことになります*4。そのようなコンテンツは外部からアクセスされても何か重要な情報が漏洩するわけでもなんでもありませんから(元々ウェブ・ページの要素として表示するための内容なので)、公開サイトのどこに置くかは勝手に決められます(ただし検索エンジンのロボットにインデックスされるのは好ましくないでしょうから、.htaccess でアクセスを制限してもよいでしょう)。

*4多くの方はご承知のように、これは普通は WordPress にキャッシュ機能を追加するプラグインで実現できます。しかしながら、フロント・エンドを WordPress とは独立させているので、ここでご紹介している事例ではプラグインが使えません。もっとも、予算に余裕があれば CDN を採用してキャッシュすることは可能でしょう。

[B] /wp-config.php の内容を退避すべきかどうか

サーバに初めて WordPress をインストールするときに、データベースのアクセス情報などを入力すると、/wp-config.php が出力されます。もともと、ダウンロード・ページから入手できる “wordpress-6.1.1.zip” のようなディストリビューションには、/wp-config-sample.php というサンプルのファイルしか入っておらず、ディストリビューションをサーバへ展開してからアクセスしたときに開始されるインストール手続きによって、/wp-config.php がサーバに出力されるわけです。

このファイルは、もちろんプロであれば中身を見たことはあるでしょうし、ディストリビューションをサーバへ展開して WordPress をインストールする際に、/wp-config.php を最初から用意して一緒にアップロードしている方もおられるでしょう。もしこのファイルを見たことがなければ、サーバからダウンロードしてテキストエディタで開いてみてください。先の第1部で「対策(6): 適正なレンタルサーバを選定すること(続)」に書いたとおり、wp-config.php には、通常は秘密にしなければならないデータベースのパスワードや、認証に使うキーコード(key)、あるいは「salt 値」と呼ばれる固有の文字列が定義されていますから、これが他人に知られるのは危険です。そして第1部では、/wp-config.php をサーバへアップロードする際の通信方法を安全にすることとか、/wp-config.php に記述する内容を安全にするといった対策を紹介しています。また、もっと後の「対策(16): その他」では、ウェブ・サーバのアプリケーションである Apache つまり httpd のアクセス制御という機能を利用して、/wp-config.php というファイル自体へブラウザからのアクセスを制限する方法を紹介しました。(残念ながら、僕は IIS や Nginx も少しは使ったことがあるものの、詳しくご紹介できるほどの実績はないので、本稿ではウェブ・サーバのアプリケーションとして Apache を使った解説しかできません。)

ところが、/wp-config.php ファイルをアクセス制御で保護するだけでは不十分だと言われる場合があります。なぜなら、httpd の動作に不備があったり、.htaccess の書き方に間違いがあると、正しく /wp-config.php ファイルへのアクセスを制御できなくなります。更に httpd と php との連携に問題が起きて、PHP のスクリプトとして処理されるべきファイル(たいていは拡張子が .php になっていますが、AddType のような httpd のディレクティブによって .html でも PHP のスクリプトとして処理できます)が正しく処理されなくなると、ファイルの中で <?php ... ?> という区切りを使って記述している PHP のスクリプトが処理されないまま、エンドユーザのブラウザにソースコードとして表示されてしまうかもしれません。

例えば、当サイトでは .html ファイルを PHP のスクリプトとして処理するように設定していますが、もしウェブサーバが .html ファイルを PHP のスクリプトとして処理できないような状況に陥ると、このページのソースの冒頭には以下のようなコードが表示されてしまいます。

<?php /* ----------------------------------------------------------------------------- file name : /archive/****.html compatibility : PHP 7.x / UTF-8 [LF] description : entry at MarkupDancing copyright : Copyright(C)2017 by Takayuki KAWAMOTO date : managed since [2017-03-02 15.59 (JST: GMT+0900) @333] modified : [2017-09-04 14.05 (JST: GMT+0900) @253] encode phrase : 時々京の方向に幅が細くて美しい線が入った飾りを持つ雀が往く encode phrase : 男は傷の拳で美しく印刷された一冊の書を持ち憎い相手の笑いに応じた encode phrase : 牀前看月光/疑是地上霜/擧頭望山月/低頭思故郷 encode phrase : 茨菰葉爛別西灣/蓮子花開猶未還/妾夢不離江上水/人傳郎在鳳凰山 [...] define( 'PUBLISHED', '2017-03-03 17:18:47' ); define( 'MODIFIED', '' ); /* ////////// definitions ////////// */ include_once( '../_tmpl/header.html' ); ?>

このような事態を防ぐには、/wp-config.php の内容を、仮にウェブサーバで問題が生じて .php ファイルがテキスト・ファイルとして扱われてしまうようなトラブルが起きた場合でも、ブラウザからはアクセスできない場所へ隔離すると安全に思えます。端的に言うと、httpd で言う DocumentRoot よりも上位のディレクトリ階層か(下位ではない)別のディレクトリへ /wp-config.php の内容を記述したファイル(/wp-config.php の内容そのままでもいいです)を置いて、DocumentRoot 直下の /wp-config.php からインクルードして読み込むわけです。もし、あなたがサイトを運営していて、サーバ内での DocumentRoot として設定されているフルパスが /var/www/html だったとすると、/var/www/wp-config.php あるいは /var/www/lib/wp-config.php などというファイルを作って、/wp-config.php へ記述するべき全ての定義を移してしまいます。そして、もともとの場所にある /var/www/html/wp-config.php では、

<?php require_once( '/var/www/lib/wp-config.php' ); ?>

と、一行だけ記述すればよいでしょう。[実は、現在の WordPress では DocumentRoot の直下に /wp-config.php がないとしても、その親ディレクトリにあれば読み込んでくれるようになっています。したがって、DocumentRoot の上の階層に置くという手法が普及しつつあります。]

ところで、日本語版の WordPress Codex でも言及されていたように、この手法には支持者と反対者がいます(とは言え、英語版の Codex では言及されていません)。反対する側の理由としては、まず Ian Dunn は “seems like a very rare occurance, and it wouldn't outweigh the downsides of exposing logs/backups/etc to HTTP requests”(wp-config.php がテキストファイルとして表示されてしまう状況は非常に稀にしか起きないと思うし、こういう状況が実際に起きたとしても、それでログファイルやバックアップファイルが HTTP のリクエストに対して漏洩してしまうという脆弱さが増えたりはしないだろう)と書きました。更に chrisguitarguy によれば、“[if] that happens, you're already in trouble: they have direct access to your server (and probably root permissions) and can do whatever they like”(もし /wp-config.php をテキストファイルとしてダウンロードしたり表示しようとしても、root の権限が設定されていれば何も表示されないしダウンロードもできない。逆に言えば、root 権限を奪われたらこんなことをしても手遅れだ)と書かれています。

確かに、本当に稀にしか起きない状況を想定して複雑な(他の問題も起きやすくなるような)対策を導入してしまうと、別のリスクが顕在化するかもしれません。しかし僕の考えでは、PHP ファイルがテキスト表示されるような状況が本当に稀にしか起きないのかどうかは、Ian Dunn の説明だけでは正否を決められません。例えばレンタルサーバの中にはファイルを PHP として処理するためのディレクティブとして “AddHandler php7.0-script .php .html” と記述しなければならない場合があります(僕が当サイトで利用しているヘテムルも、このように記述しないとファイルが PHP として処理されません)。これを、一般的な記述方法である “AddType application/x-httpd-php .php .html” と取り違えるだけで、ここで懸念している状況は簡単に発生してしまいます。サーバが 500 を常に返してレスポンスを拒絶してくれるとは限りません。

次に、/wp-config.php ファイルが 0400 のパーミションでアクセス制御されていれば .php ファイルが PHP のスクリプトとして処理できなくなる状況に陥っても問題ないという反論については、ここで問題にしているのは httpd の挙動に問題が起きた時の話なので、httpd の残りの機能が正しく働く保証があるかどうか疑わしいと言わざるを得ません。情報セキュリティに限らず、アプリケーションの設計に際しては、事故が起きたときにフェイルセーフやフールプルーフというポリシーを採用することが(プロには)求められます。WordPress を使ってウェブサイトを運用させる際に、その運用条件を幾つかのレイヤーに分類しておくと、以下のようになります。

これらがそれぞれ誤作動なり停止した場合を想定して、ディフォールト(つまり何も処理できなくなった場合の処理)としてどうなるかを知っておけば、ディフォールトでも安全なように設定したりファイル構成を決めおけば良いでしょう。/wp-config.php を DocumentRoot よりも上の階層へ退避させるというアイデアは、もちろんフェイルセーフの発想です。httpd, php が動作しなくなって .php ファイルがテキスト・ファイルとして処理されてビジターの UA へ送り返され、丸ごと画面に表示されたとしても、インクルードしかしていなければ、ブラウザに表示されるのは <?php require_once( '/var/www/lib/wp-config.php' ); ?> という文字列だけであって、wp-config.php のパスは漏洩しますが、データベースの接続情報などは保護されます*5。“/var/www/lib/wp-config.php” という場所が漏洩するのは良くないことですが、データベースのパスワードがそのまま漏洩するよりはマシというものでしょう。[そして更に、2023年の時点では DocumentRoot にインクルードするためのファイルを置く必要すらなくて WordPress のコア・システムが勝手に上の階層で wp-config.php を探してくれるため、更に状況は改善されています。]

*5更に Aaron Adams の投稿も参考にして、幾つかの反論へ応えておきましょう。

まず「ファイルが PHP として動かなくなれば攻撃者がアップロードしたり埋め込んだり書き換えた PHP コードも動かなくなるので、どのみち漏洩しても大した被害はない」という反論がありえます。仮にデータベースのパスワードが漏れたとしても、データベースのサーバがプライベートの IP アドレスでしかアクセスできないネットワーク構成で配置されているなら、攻撃者は PHP が動くウェブ・サーバから攻撃するしかないので、PHP が動かないウェブ・サーバに何をやっても無駄だというわけです。しかし、それはデータベース・サーバがプライベートな IP アドレスしかもっていないという条件に依存した反論です。もちろん、データベース・サーバにグローバルな IP アドレスを割り当てない方がよいという考え方は妥当だと思いますが、そうでない条件でも通用するような対策を採用する方が汎用性はあります。反論して否定する理由にはなりません。また、実質的に攻撃に使えないからといって、サーバ内のファイルに記載されている機密情報が漏洩したという事実を軽視するような態度は、情報セキュリティを担う者として非常に危険だと思います。

次に「/wp-config.php の内容を別のディレクトリに動かすと WordPress の挙動に問題が起きるのではないか」という懸念があるかもしれません。Codex では「wp-config.php ファイルを、WordPress をインストールした階層の一つ上に移動することができます」と説明していますが、そこには一定の(説明に現れない無自覚の)条件が仮定されており、もしあなたのサーバでそういう条件が満たされなければ WordPress は動かないかもしれないわけです。しかし、このような懸念は、そもそも「/wp-config.php の情報を退避した方が良い」という対策の反論になっているのでしょうか。あなたのサーバで /wp-config.php の情報を別のファイルに移すと WordPress が動かなくなったというのであれば、まずやるべきことは、その原因の特定です。もし php.ini の設定で include, require 系の関数が使えなくなっているなら、そもそもそのウェブサーバでは WordPress どころか一般的な他の CMS も動かない筈です。そのような状況を除外しているからといって、「/wp-config.php の情報を退避した方が良い」という対策を(あなたのサーバに適用できるかどうかに関わらず)提案できないと反論する根拠にはなりません。

[G/B] KUSANAGI の対策について

WordPress の本を出してからサポート記事を公開されていたプライム・ストラテジーさんでは、WordPress を稼働させるための仮想 OS として KUSANAGI というプラットフォーム(のイメージ)を提供されています。AWS, Microsoft Azure, さくらのクラウドといったパブリック・クラウドのサービスから Docker や VMWare のようなソフトウェア・レベルの仮想化環境や VPS にも対応しており、WordPress をあらかじめ高度にチューニングされた環境で動かしたい場合に有効な選択肢と言ってよいでしょう。もちろん、他の会社も最初から AWS の EC2 などに WordPress を実装した状態で提供されているサービスなどを提供しているので、使いたいプラットフォームが決まっていれば、他にも選択肢はあります。ここではプライム・ストラテジー社や KUSANAGI の宣伝を目的として言及しているわけではないため、必要以上の詳しい説明は省きますが、どのようなサービスなのかは以下の内容を理解するときに役立つと思いますから、KUSANAGI がどのようなサービスなのか、既に読者はご存知であると仮定して議論を進めます。

さて、KUSANAGI のサイトでは WordPress の実装や運用について、「推奨するセキュリティ対応」という文書を公開されています。ざっと眺めた限りでは、サーバへターミナルでアクセスして wp-config.php ファイルの設定内容を http / https ではアクセスできない別のディレクトリへ退避するという、当サイトでも上記のセクションで推奨した方法を解説されています。僕も、いままさに関西の鉄道系企業の受託案件でウェブ・サーバへ実装している WordPress については、同じように DocumentRoot とは別のディレクトリへ wp-config.php の設定内容を退避して運用しています。学ぶべき点が多い文書だと思うので、このセクションでは、上記の文書で解説されている方法を紹介しながら、僕なりのコメントを追加しておきたいと思います。コメントの内容によっては、受託案件などでしか通用しない話題もあるため、インジケータは G と B の両方をつけておきました。レンタル・サーバなどでサイトを運用する方には関連性が低い(だからといって、レンタル・サーバの運用のレベルが低いとは限りません)コメントも含まれていますので、試す必要がないことを議論している場合があります。

KUSANAGI のサイトで解説されている対応は、以下のとおりです。

  1. wp-config.php の安全性を高める [G}
  2. WordPress の管理画面に対してアクセス制限を行う [G]
  3. 接続元の IP によるサーバーへのアクセス制限 [B]
  4. ディレクトリのパーミッション設定 [G]

以上の対策のうち、接続元の IP アドレスを指定して、サーバで動作する各種のサービスにアクセス制限を追加するという対策は、KUSANAGI のように利用者が OS の管理者権限をもっていない限り、レンタル・サーバや ASP (application service provider; WordPrss.com もその一つ) によって WordPress を利用している状況では不可能な対策です。それ以外は、1. や 2. は殆どのレンタルサーバで実施できるでしょうし、4. についてはレンタルサーバだと DocumentRoot の上層にあたるディレクトリのパーミッションを変更したり、新しいディレクトリを作成する権限がない場合もありますが、多くのレンタルサーバでは実行できる対策でしょう(僕が当サイトを公開しているヘテムルではできます)。

では、一つずつ取り上げていきます。まず最初に wp-config.php の内容を別のディレクトリへ退避するという対策は、既に本稿では是非も含めて取り上げている話題なので、あまり具体的には説明しません。ここで特に重要なのは、バージョン 2.6 以降では wp-config.php を Wordpress のインストール・ディレクトリ(ルート・ディレクトリ)から一つ上の階層へ移動させても自動で探してくれるようになっていることです。/wp-load.php のソースを見ていただくと、ABSPATH . 'wp-config.php' に wp-config.php があるかどうかを判定して、なければ dirname( ABSPATH ) . '/wp-config.php' を探していることが分かります。

では、ここで httpd や php の動作に問題があって PHP のソースがパースされずにブラウザで丸出しになってしまった状況を考えましょう。もちろん、一つ上の階層へ設定内容を退避するだけでも十分な効果はあります。ウェブサーバのトラブルがシェルコマンドを実行するような効果をもつのでない限り、PHP が動かなくなればファイルのインクルードができなくなるので、DocumentRoot の上層どころかあらゆるパスのファイルをインクルードできなくなるため、ファイルのインクルードという(処理系にとっては飽くまでも正常な)動作を利用した攻撃もできなくなるからです。なお、僕の実装方法では、自動的に上の階層の wp-config.php を探すというやり方ではなく、/wp-config.php ファイルはそのまま DocumentRoot に配置し、そのファイルに設定内容が記述されているファイルのパスを書いています。僕が wp-load.php で勝手に探索させる方法よりもパスを自由に指定する方が望ましいと考えている理由は、こちらの方が自動化された攻撃に強いと思われるからです。WordPress の動作仕様として wp-config.php のパスが何種類であろうと最初から決まってしまっているなら、WordPress のソースコードは誰でも内容を読めるので、仮にシェル・コマンドすら実行できてしまうようなリスクがサーバにあるとき、あらかじめ決まったパスにある wp-config.php へしらみつぶしにアクセスするだけで DBMS へのアクセス情報などが奪われてしまいます。これに対して、/wp-config.php に設定内容が書かれたファイルへのパスが書かれているかもしれないという前提でアクセスしてくるプログラムは多くないと思われるので、自動化された処理で設定内容を記述したファイルへアクセスされる危険は少なくなると思えます。もちろん、そのサイトなりサーバをクラックするつもりで人間が攻撃していれば、/wp-config.php の中身を見ただけで判断がつくため、その場合は僕のやり方でも KUSANAGI の文書に書かれている対策と同じていどの強さしかないでしょう。いずれにしても、情報セキュリティ対策の基本的なアイデアや原則として知られているように、情報を隠すことで達成できる安全は脆弱なのです。

次に、管理画面へのアクセス制限は WordPress のセキュリティ対策について書かれた素人のブログ記事でも紹介されている話なので、特にその重要さを強調しなおす必要はないでしょう。ただし、自宅の IP アドレス(つまり固定 IP アドレスのオプションを契約してインターネット通信していない場合)で接続元を制限する場合に、プロバイダから割り当てられている IP アドレスというものは一定の頻度で変わってしまうということを知っておかなくてはいけません。そして、IP アドレスのクラス C が変わるていどだからといってサブネットマスクを指定して広範囲の IP アドレスを許容してしまうと、実質的に IP アドレスで制限している意味がなくなります。また、こんなことは中学生でも知っていることだと思いますが、スマートフォンで WordPress の管理画面にアクセスするなら、IP アドレスでの制限はできないと思った方がいいでしょう。なぜなら、スマートフォンの通信キャリアの IP アドレスはすべての契約者で共有しているからです。したがって、商用で WordPress のサイトを制作しているとき、クライアントにスマートフォンで表示確認してもらうときは、ベーシック認証を使うか、WordPress のプラグインでパスワードを必須にする(つまりログインしなくてはいけない)しかありません。

それから 3. の接続元 IP アドレスによるアクセス制限は、ssh, ftp, scp などサーバの運用において必要なサービスだけを起動して、アクセスを許可してよい IP アドレスだけを許容するとよいでしょう。KUSANAGI の文書では hosts.allow, hosts.deny ファイルを使っていますが、もちろん自力でサーバを構築しているような案件でセキュリティを設定する技術者であれば、Linux だろうと UNIX だろうと、Windows Server だろうと、あるいはディストリビューションの違いに関係なく、侵入検知システム(IDS)やウェブ・アプリケーション・ファイアーウォール(WAF)や iptables, ossec などのファイアーウォール・ソフトウェア、ないしはネットワーク通信のサービスとして用意されているアプライアンスとしてのファイアーウォールを利用してもよいでしょう。昨今では、廉価なレンタルサーバでも WAF を提供しているところが増えていますので、下手に専用サーバで構築された WordPress よりもレンタルサーバで運用されている WordPress の方が堅牢だと評価できる場合すらあります。

最後に 4. のパーミッションですが、これは WordPress を実装する条件によっていくつかのパターンがありえます。たとえば、いちど公開したコンテンツを固定しておき、後から画像や PDF ファイルをアップロードすることが絶対にないというなら、/wp-content/uploads に書き込み権限をつける必要はないでしょう。KUSANAGI ではインストール直後には /wp-content のパーミッションが 0777 になっているとのことですが、これはセキュリティの観点からは非常に危険だと言えます。特に、誰がプログラムを書いたのかも分からないテーマやプラグインをインストールするなら、プラグインに未知の脆弱性があれば、そのファイルを起点として wp-content 以下に好きな PHP ファイルをアップロードして実行させるといった攻撃が可能になってしまいます。したがって、文書にあるとおり 0755 へと変更することが望ましいでしょう。そして、プラグインやテーマを追加するときは、いったん自分のマシンに ZIP ファイルをダウンロードしてから、SFTP や scp over ssh でウェブサーバにアクセスしてファイルを転送するようお勧めします(つまり、管理画面でのオンデマンド・インストールはやめましょう)。

最後に KUSANAGI の文書を評価すると、対策が四つしか紹介されていないのは、恐らく KUSANAGI として用意されているアプライアンスの実装段階でいくつかの対策が加えてあるからかもしれません。なんにせよ、さしあたっては上記の対策を施すだけでも、レンタルサーバや専用サーバで WordPress をもっと安全に実装し運用できるでしょう。したがって、KUSANAGI の文書で紹介されている対策は、どれも推奨できるものとしてご紹介できます。なお、同じ考え方は他の CMS にも応用できると思うので、このような話題は WordPress だけではなく、他の CMS の開発者やユーザのコミュニティとも連携したいところです(そういう事例が殆どなく、WordPress, Drupal, Movable Type などとバラバラに議論されているのが残念です。更には有償の、得てしてメンテナンスやコンサルティング料金も合わせて非常に高額な CMS だと、セキュリティ対策や実装内容は知財に関わるので、ロックインすることが命綱であるような企業の協力は全く期待できません)。

[G] ペネトレーション・テスターの視点

情報セキュリティ対策や技術の分野で知られる京セラコミュニケーションシステム株式会社(KCCS)さんは、WordPress や PHP のセキュリティについて著書やブログ記事でも知られている徳丸浩さんが在籍していた会社でもあり、僕らのような一介のプログラマや情報セキュリティマネージャの中でも、短い一時期ではありながら「東の楽天、西の京セラ」と言われていたのを覚えています。当然ながら WordPress ていどの CMS をクラックできるスキルを持った技術者は新卒にすら何人かいると思いますし、逆にその程度のスキルすらもっていない人は採用していない筈です。よって、「ペンテスターが語る攻撃者の視点 ~WordPress編 前編~」や「ペンテスターが語る攻撃者の視点 ~WordPress編 後編~」のような記事は、実際にサイトのセキュリティ・レベルを監査したり検証する側からの議論として興味深い内容があるので、一読をお勧めします [2023年現在、どちらのページも京セラのサイトからはなくなり、SecureOwl というサービスも京セラコミュニケーションシステムでは提供されていないようです]。それこそ、「ワードプレス」とカタカナで書いてあるような素人のコピペ記事を 100 ページ読むよりも有益でしょう。なぜなら、実際問題として WordPress が攻撃しやすい最大の理由は、ミドルウェアやソースコードやファイル・システムの扱い方を何も考えたり知らずに FTP でファイルをアップロードしたり、レンタルサーバでボタン一つクリックするだけで導入できてしまうようになったからです。それに輪をかけて、「ワードプレス」などとカタカナで表記しているていどの記事を書いているような素人(これは飽くまでも目安ですが、プロダクト名を原語ではなくカタカナで表記しているのはたいてい素人です。その典型は、いつまでも「ユーチューブ」などと書いて平気な新聞記者でしょう。何のつもりかは知りませんが、高齢者がアルファベットを視認できず、英単語を読めないとでも思っているのでしょうか)までもがセキュリティ対策の記事やプラグイン開発について記事を乱造している始末です。

従って、「WordPressで構築したサイトは攻撃の難易度が低くなる傾向にあるといえます。」「そのため、WordPressで構築したサイトであることが知られると、サイト改ざんなどの攻撃のターゲットになるリスクがあります。」(上掲、「前編」)と評価されているのは仕方のないことだと言えるでしょう。しかしだからといって、この記事で紹介されているような幾つかの特徴について、胡麻化したり難読化したり隠蔽して、WordPress で構築されたサイトであるという事実を隠す必要はありません。これは、当サイトで他の記事でも書いていることですが、サイトやサーバの特性を完全に隠すことは極めて難しいか、たいていの人には不可能であって、そういう情報の隠蔽という条件にもとづいて情報セキュリティのレベルを維持するような対策は、現実的でもありませんし、実はたいていの場合に安定してセキュリティレベルを維持できるほど適切な対策でもないのです。

まず「前編」の (1) で紹介されているように、WordPress で構築されているという事実を隠蔽するために、管理画面へのアクセスを制限するという手法を採用したとしましょう。FQDN の直下に WordPress のコアシステムをインストールしていると仮定して、FQDN/wp-login.php というファイルへアクセスできるかどうかによって WordPress を使っている事実が推定できないように、このファイルをプラグインで任意のファイル名に変更したとしましょう。記事では乱数を付けたファイル名に変更している事例を紹介していますが、ファイル名をまるごと任意の文字列に変更するプラグインもありますし、特定のクエリ文字列を使ってアクセスすることを要求するような mod_rewrite の設定もできます。そして、エラーを返すときに WordPress の機能を使ってエラーを返すなどという間の抜けた手法は使わず、単にウェブサーバの標準的なレスポンスとして 404 を返すようにします。しかし、FQDN/wp-login.php へのアクセスさえ 404 を返しておけばいいのかと言えば、そんなことはありません。FQDN 直下には、WordPress のコアシステムに含まれるコードとして、FQDN/wp-load.php は存在すれば何も表示しなくても 200 を返しますし、自動でインストールした WordPress のサイトであれば、FQDN/license.txt のような単なるテキストファイルが必ず展開されているはずです。要するに、「~というファイルにアクセスできなければ WordPress はインストールされていない」という条件を、一部のわずかなファイルについてだけ決めて判断しているような(或る意味では程度の低い)攻撃者や単純な動作仕様のボットが相手ならともかく、そのサイトを目当てに色々なやり方を考えて狙ってくるほどの攻撃者には殆ど無効と言える対策だと思います。

次に「前編」の (2) で紹介されているような、出力されたソースから判断する手法について検討してみましょう。この場合も、/wp-content のディレクトリ名を変更することは簡単です。

define( 'WP_CONTENT_DIR', $_SERVER[ 'DOCUMENT_ROOT' ] . '/PATH' );
define( 'WP_CONTENT_URL', $_SERVER[ 'HTTP_HOST' ] . 'URL for website' );
define( 'WP_PLUGIN_DIR', WP_CONTENT_DIR . '/PATH-TO-PLUGINS' );
define( 'WP_PLUGIN_URL', WP_CONTENT_URL . '/URL for plugins');

このように定義すればディレクトリの名前どころか任意のパスや URL を使えますし、もちろん Alias などを使えば、パスは DocumentRoot の下層ですらなくなるでしょう。したがって、ソースに “/wp-content” のような文字列があるかどうかを検索するだけで判断しているような、これも程度の低い攻撃者やボットからの攻撃であれば防げるのかもしれません。更に、僕が上記で紹介しているように、WordPress を管理画面としてだけ導入するという手法を採用している場合には、公開側は WordPress と限りなく無関係に構築できますし、ソースの表示においても WordPress の動作を推定させるような痕跡は最小限にできます。なぜなら、そういうサイトのコードでは、require( './wp-path/wp-blog-header.php' ); だけ実行できればいいからです。もし WordPress のテンプレート・タグと呼ばれる専用の関数を全く使わないなら、wp-blog-header.php の読み込みすら不要にできます。そして、管理画面用にインストールしている WordPress は、そのインストール・ディレクトリ全体をベーシック認証や IP アドレスの制限などによってアクセス制御すればよいでしょう。

そして「前編」の (3) で紹介されているリソースからの推定は、確かに参考になるものですし、このように些細な兆候からでも色々なことを推定されてしまうという実例として役に立ちます。そして、多くの WordPress サイトでは出来合いのテーマを使っているだけなので、WordPress のテーマにありがちなデザイン要素という特徴で推定されるのも無理はないと言えます。ですが、このような着眼点は対策として考慮するには些末すぎるという問題があります。第一に、われわれのように電通案件で高額な費用をかけてサイトをデザインしたり構築している事業者では、出来合いのテーマを使ってサイトを構築することは 100% ありませんし、第二に、このような特徴で WordPress を使っていると推定されてしまうような素人のサイトであれば、もっと他に注意すべき点があるはずだからです。わざわざ情報セキュリティ対策のためだけにスタイルシートの記述内容を工夫する配慮ができるくらいなら、WordPress を使っていると知られても簡単には攻撃されないような対策として、他にやるべきことがあるでしょう。

「後編」になると、具体的に WordPress のサイトを攻撃する手法が紹介されています。その一つ目として、ユーザ名を推定する方法が幾つか挙がっており、本稿の第1部でも既に説明している “username enumeration” という手法でリダイレクトしたり、RSS フィードの値などから調べる方法が紹介されています。人によっては、ユーザ名など漏洩してもいいと言っているようですが、これも同じ箇所で僕が書いたように、相手に手間をかけさせることも対策の一つであり、難度が上がれば上がるほど攻撃され難くなると期待できます。RSS などでユーザ名は簡単に漏洩するのだから、ユーザ名を “admin” から “UqFycNAsojUh9vaH” のような文字列に変更する意味はないと論じるのは、僕は暴論だと思います。そのような対策を実行するために予算や手間や時間が膨大にかかったり、あるいは一定の頻度でメンテナンスを継続しなければいけないというならともかく、WordPress をインストールした際に数分程度の手間をかけることに是非を言うなど、個人が勝手に公開してるサイトなら好きにすればいいのでしょうが、企業として仕事でやっているエンジニアにそんなことを思わせてしまう役職者は、リーダーとして無能だと思います。そして、情報セキュリティの事故というものは、結局は個々の(概してスキルのない)従業員が最大かつ根本的な原因なのではなく、思慮の浅い無能な管理職や経営者が本当の原因なのです。この他、Ruby で実装されたスキャン・ツールの紹介と XML-RPC PingBack を利用した攻撃への対策だけなので、特に取り上げません(いまどきトラックバックなんて活用してる人がいるんでしょうか)。

冒頭に戻る

第3部(補遺2)

本稿は、2016年の2月に更新を終了した「WordPress のセキュリティ対策」という記事の二つ目の補遺です。一つ目の補遺だけでも既にかなり長い文章となっているのに加えて、今回は IPA の『安全なウェブサイトの作り方』というガイドラインに付属しているチェック・シートの項目を全て取り上げるため、新しく起稿してページを分けることとしました。そのため、当サイトで掲載している文書で議論した論点の幾つかについては、既に読者がご存知のこととします。

それから、このページの議論を適切に理解して利用するためには、プロダクション用途(つまり業務)でのプログラミング経験とサーバ構築の経験が3年以上はあることが望ましく、特に PHP と WordPress については独特の用語(“the Pages” など)や取り回しの手順について知識が必要であり、レンタルサーバで自動インストールした経験しかない方には難しい内容だと思います(ただし、そういう方にもできるセキュリティ対策はあります)。また、本稿は僕自身がナショナル・クライアント案件(具体例は NDA があるので挙げられませんが、要するに電通グループ、博報堂DYグループ、JR西日本コミュニケーションズといった広告代理店さんが手がける上場企業や大企業のウェブサイト制作・構築)で実装した経験がある技術者であると同時に、本来は望ましいことではありませんが、その実装状況を監査する情報セキュリティ部門の役職者でもあるため、コピペで終わるような小手先の対策だけではなく、情報資産やパーソナルデータの保護、あるいはシステム全体のライフサイクルという観点でも議論します*1

*1まず強調しておきたいポイントは、『安全なウェブサイトの作り方』の基準を満たしていても情報セキュリティ対策としてぜんぜん十分ではないということです。特に、情報セキュリティマネジメントの実務家として強調しておきたいのは、このようなチェック・シートだけが独り歩きして納品時の保険であるかのように扱われてしまっては、重大なリスクを放置しかねないということです。なぜなら、このシートの項目は情報セキュリティマネジメントで必要とされる四つの対策のうち、「技術的対策」と呼ばれる一種類(しかも僅かな一部分)の対策にしか対応していないからです。たとえば、FTP アカウントの管理はどうでしょうか。ステージング環境(テストサーバ)を別につくるとき、アクセス制御はどういう方針で組み込みますか。『安全なウェブサイトの作り方』には、そういう別の種類の対策が他にたくさんある(技術的対策としてすら他にも膨大にある)という示唆すら満足に説明されていません。『安全なウェブサイトの作り方』は、技術的対策の中でも重要なポイントをしっかり取り上げているという理由で高く評価できるガイダンスですが、情報セキュリティの対策としては、しょせん一部でしかないということも事実なのです。

このように書くと出てきやすい反論として、次のようなものがあるでしょう。「それでも、いまだにパスワードをテキスト形式で保存したり、個人情報を HTTPS 通信なしで取得するフォームを実装するような業者がいるのだから、まずこれだけでも普及させることが重要だ。仮に『安全なウェブサイトの作り方』で書かれた対策が一部だとしても、そもそも情報セキュリティ対策の『全体』を知っている人間などいないのだから(「シュナイアーの法則」のバリエーション)、できることから始めるべきであり、体系的な話を強調するだけでは実効性がない。」その反論の前提については、確かにそうです。過去に当サイトで掲載していた「ウェブ業界はどこを向いているのか」という文書でも議論しましたが、僕の見立てでは、まず 99.9% のウェブ制作会社や開発会社の「エンジニア」や「ディレクター」を自称している人々は情報セキュリティについての(技術的対策についてすら)リテラシーがありません。それどころか、セキュリティの知識だけではなく、プログラマやサーバ技術者としてすら疑問を感じるような人も数多く見かけます。すると、そういう人々でも何かの案件で働いてしまっているからには、簡単にすぐ利用できる実践的な業務や手順を教えて実行させることが重要だと言えるかもしれません。

しかし、だからといって体系的な知識や見取り図のようなものをもつことが無駄だという話にはなりませんし、後回しでいいとすら思いません。どれほど「実践的」と言えようと、僕は目先の処理や対処に偏向した考え方は間違っていると思いますし、そんなものを「現場主義」などと言って正当化する人々は単なる無知無教養を考え方や方針の違いであるかのようにミスリードする夜郎自大にすぎません(これは僕が哲学者として考えていることですが、いわゆる「理論と実践」などという対比そのものが何らかの思惑による偽の対比なのです。僕ら自身が実例であるように、有能な人間は理論も実践もどちらもできます)。そして、実は情報セキュリティだろうとマーケティングだろうとビジネス英語だろうと、「実践的」などと称してばら撒かれてきた本や雑誌記事は、既に何十年も多くの人々に利用されてきています。つまり、日本のウェブ制作や開発という業界の現状は、既に「実践的」なシステム開発やコーディングや情報セキュリティ対策やビジュアルデザインを20年ほど続けてきた末の結果なのです。したがって、「実践的」なノウハウが必要だといま言っている人たちは、それまでに「実践的」なことだけしかやってこなかった人々の結果として、「実践的」なノウハウがいまだに必要とされるという、殆ど向上も改善もしなかった業界という現状を正しく理解していないと言えます。こういう人々は、僕に言わせれば「ママ、おなかへった」という子供と同じです。

僕は、これは学校の勉強にも言えることだと思いますが、要するに原理・原則という知識を体系的に学ばず小手先のテクニックやノウハウだけを次から次へと消化していくような人々には、そういう「実践的」と称する場当たり的なルーチンワークというレベルを超えた仕事はできないのです。もちろんブルーカラーとして作業していればいいという人には、それを超える知識も発想も必要ありませんが、僕らが本稿で議論しているのは、他人様に成果物の責任を負える立場の人が何をどう考えて実行すればいいか(すべきか)という話であって、企業において責任を負えない役割の人が WordPress を使って何をどうしたいかなど知ったことではありません。

『安全なウェブサイトの作り方』について

国内でウェブ・アプリケーションを受託開発している事業者の多くが既にご存知のとおり、独立行政法人情報処理推進機構(IPA:Information-technology Promotion Agency, Japan)という団体があります。多くのプログラマが取得している「基本情報技術者」とか「応用情報技術者」とか「情報処理安全確保支援士」のような国家資格の認定制度を運営していたり、情報セキュリティ施策に関する研究や啓発活動や人材育成など、多くの活動内容で知られている組織です。この IPA から2006年に初めて公表されたのが『安全なウェブサイトの作り方』というガイダンスです。その後も改訂が続けられていて、最新は第7版(2021年3月、第4刷)となっています。本稿では第7版第4刷の内容に沿って、一部を議論します。

『安全なウェブサイトの作り方』は、オンラインでは情報セキュリティの研究者、実務家、技術者、あるいは啓蒙家として知られている、高木浩光さんや徳丸 浩さんといった方々が中心となって執筆しており、もちろんその手の有名人だけではなく、開発ベンダーやセキュリティ企業等で実務に携わっている方々も協力して作成されたガイダンスです。対策としての厳格さには色々と議論の余地はあるかもしれませんが、最低限度の対策を集めたガイダンスとして「標準的」と言ってよい内容だと言えます。もちろん、他にも OWASP という団体が発行している “OWASP Application Security Verification Standard”(JPCERT による日本語訳)やデータベース・セキュリティ・コンソーシアム(DBSC: DataBase Security Consortium)が発行している「DB 内部不正対策ガイドライン」(2016, HTTPS ではないので注意)や官公庁が発行するガイドライン、それから他にも法令や規格など数多くの規範や規格やガイドラインがあります。その中でも、特にウェブ・アプリケーションの開発において留意すべきポイントを押さえてあり、具体的な対策の方針も提案しているガイダンスとして、『安全なウェブサイトの作り方』は既に多くの開発ベンダーやウェブ制作会社で導入されており、実際に僕の所属企業でも大手の広告代理店さんから『安全なウェブサイトの作り方』に付属するチェック・シートをもとに作成された文書への記入・提出が求められることがあります。(もちろん、まったくの使いまわしではありませんが、項目は用語法も含めてほぼ同じです。)

さて、納品物としてのウェブサイトや CMS について一定の業務を遂行した結果につき、受託企業がしかるべき範囲の動作品質を保証するべきことは言うまでもありません。厳密には、スタイルシートや動画といった「アプリケーション」とは呼べないコンテンツについても、受託している制作会社は事前に合意した範囲の成果物について、一定の「契約内容不適合責任」(改正民法第562条, 第566条, 第636条)を負っている場合があります(旧民法で「瑕疵担保責任」と呼ばれていたものです)。法律で規定されている「責任」の範囲は、もちろん「成果物」や「品質」を契約でどう定めるかによって決まりますので、善管注意義務の範囲と言えるような業務上の責任はともかく、本来は制作業務に着手する前にどこまでが制作会社の責任なのかを決めておくべきです。ビジュアル・デザインのように前もって売り手と買い手で「品質」として同意できる客観的な基準が存在しない場合もありますが、WordPress を使ったウェブサイトの構築といった案件の場合には、「『安全なウェブサイトの作り方』に付属しているチェック・シートの項目を満たさなくてはならない」などと具体的な基準を定める事前の合意は可能ですし、当社も外部の会社に委託する際は、取引を開始するかどうかの与信判断において「情報セキュリティベンチマーク」というチェック・シートへの記入を求めたり、開発業務を委託する場合は『安全なウェブサイトの作り方』で示されている対策へ準拠することを求めています*2

*2このような基準を(それこそセキュリティ対策から内部統制どころか法令すら)軽視するような主旨の発言を繰り返す起業家や投資家は後を絶ちませんし、東京とか言われる東アジアの僻地では上場企業の取締役ですら、脱法的なマーケティングやサービス展開を推奨する愚かな発言を繰り返す人物がいるようですが、僕のような事務方の爺さんですら大企業案件レベルに対応できるセキュリティ品質で片手間に WordPress のサイトを構築できるというのに、ウェブ・アプリケーションの開発で食べている専任の人たちが、「クリエーティブ」でもなく「イノベーション」でもなく「ものづくり」でもないレベルの受託業務であろうと、対応できない筈はありません。僕がいくら有能な人材だからといって、特別に厳格な基準で仕事をしているわけではないのです。そういう、語弊はあるでしょうが、僕の尺度で言えば手抜きで達成できるような品質すら満たせない「エンジニア」って、いったい会社で何をやっているんでしょうね。

ウェブ制作会社は WordPress サイト全体の挙動に責任を負えるのか

まず結論から言うと、WordPress で構築したサイトは『安全なウェブサイトの作り方』で示されている基準を全て満たせるとは限りませんし、全て満たすべきかどうかも自明ではありません。なぜなら、WordPress で構築したサイトの全体(つまり自社で制作したテーマのテンプレートや画像だけではなく、/wp-admin や /wp-includes など WordPress のコア・システムも含む)を適用範囲にすると、恐らくウェブ制作会社は『安全なウェブサイトの作り方』の基準を満たすために莫大な費用を使うことになる筈だからです。そしてその最大の理由は、やはりオープン・ソースとして無料で手に入るソフトウェアを使うから案件の予算が安くつくと短絡してしまう、発注企業、広告代理店、ウェブ制作会社のディレクター、それどころかコーダやプログラマですら、そう思っているかもしれないということです。しかし、まじめに『安全なウェブサイトの作り方』の基準を WordPress 全体、とりわけコア・システムの監査にも向けるとすれば、誰がその挙動の品質を保証できるほど自力でコードを把握したり検査しているのでしょうか。ウェブ制作会社で WordPress のコア・システムの PHP ファイルを具体的にソース・コードとして読んだことがあり、隅から隅までどう書かれているかを把握している人など殆どいないでしょう(恐らく、情報セキュリティ会社の人々や、WordPress 専門のホスティング・サービスを提供している会社の技術者、それどころか WordPress について本を書いている人々ですら、そんな人は殆どいないと思います)。そして、もし『安全なウェブサイトの作り方』を画一的に全ての納品コードへ適用するというのであれば、WordPress 全体のソース・コードにもセキュリティ監査が必要となり、それを自社では実施できないと言って外部の情報セキュリティ監査の会社へ委託したとすれば、そんな監査を本当にやった会社さんがあるかどうかは知りませんが、WordPress という CMS を監査する委託費用だけで数千万円になると僕は想定しています*3。そして、その費用は WordPress という CMS の脆弱性を検査するだけの費用であって、httpd や php のようなミドルウェアの検査費用は含まれていませんが、『安全なウェブサイトの作り方』ではそれらのミドルウェアについても脆弱性へ対策するよう求めているわけです。

*3「数千万円」という言い方で、単純に驚かしているわけではないという根拠を述べておきます。ウェブ・アプリケーションの監査には、以下のとおり、おおよそ三つのパターンがあります。ウェブ・アプリケーションの監査では、リモート・アクセスというクライアント・サーバ・システム(C/S)の特性を前提にした実務が想定されるので、スタンドアロンのデスクトップ・アプリケーションや C++ で書いたプログラムのような条件とは違って、ふつうは監査する側がソース・コードにアクセスできません(脆弱性によってアクセスできてしまうことはありますが)。最後の三つ目のような監査は、相当な予算のある企業や地方公共団体や行政のプロジェクトか、あるいは監査する側が大きな責任を負える特殊な状況(例えば監査会社が監査先の子会社である場合など、何らかの資本関係がある場合)だけで行えるものです。ウェブの制作会社が何の担保もなしに請負契約と同等の責任で受託するのは、端的に言って愚かでしかありません。WordPress のようなオープンソースのアプリケーションを使ってウェブサイトを制作・構築する場合は、それを委託者の代わりに使ってサイトを仕上げるという業務委託契約が当たり前であって、請負契約になると一定の基準で安全と言えるウェブサイトを完成させるという結果を保証しなくてはならず、単なるウェブの制作会社にそんな能力などまずありません

上から順番に費用が上がっていくのはお分かりだと思います。最初のツールを使った検証なら、Tenable Nessus, Burp, Nikto といった脆弱性検査ツールの多くは無料で使えるため、勉強するつもりがあれば誰でも自分でウェブサイトの監査ができるでしょう。また、独自のツールを開発している企業も多く、そういうところへ委託すればツールの使い方を自力で勉強しなくても済みます。ただし、自動化されたサービスやツールは対応できる範囲に限りがあり、手動で実施するペネトレーション・テストのようなものはできないと考えた方がいいでしょう。

次に、そのペネトレーション・テストは、その技能だけで特別な国際資格があるほどのスキルが要求されるものであり、簡単に言えば自社のサイトを監査の名目で攻撃してもらうわけです。詳しく分類するとホワイトボックス試験など幾つかありますが、どれであれ技術者が専門知識と高度なスキルで精密に監査することに違いはありません。もちろん、実装しているアプリケーションがあまりにも愚かな設計やコーディングで動いている場合、自動化された監査にも言えますが、その監査用のリクエストを処理することによってウェブサイトのデータが破壊されたり大きく書き換わってしまう可能性があるため、もちろんこのような監査はウェブサイトを公開する前に実施しなくてはなりません。そして、httpd などミドルウェアの挙動にも影響を与えてしまう結果が生じるリスクもあるので、このようなテストは他社との共有で使っているレンタルサーバなどでは実施するべきではありません。ペネトレーション・テストの費用は、例えば大手の株式会社ラックさんの解説にもあるとおり、専任の技術者が数か月もかかる工数で丁寧に検査するのが標準的なので、おおよそ2,000万円弱というのが妥当な費用だと思います。弊社でもディレクターやデザイナーを代理店あるいは個別の案件について専任で貼り付けている場合は、最低でも数千万円のオーダーで売り上げをコミットしてもらうのが当たり前ですから、別に驚くような費用感ではありません。しばしば Twitter などでは就業経験のない、いやあるとしても財務という考え方やコスト感覚がない平社員、それから自分にとって高額な費用がかかる事柄を味噌もクソも「既得権益」だとレッテルを貼ってコスト・カットしようとするリバタリアンなどが、お茶の間感覚で安いの高いのという話をしますが、真面目で冷静に仕事へ取り組む人間は無視するべきでしょう。

最後に、サーバの root 権限も渡したり、あるいは自社の事業所に設置されていたり他社のデータ・センターにハウジングされているコンピュータを直に使ってもらうような前提で、OS レベルの検証から依頼するような非常に高度な監査を依頼するのであれば、これは監査だけではなくコンサルティングに近い業務となりますし、そこまでコミットしているからには結果に対して監査する側の企業は大きな責任を負うことになるので、当然ですが何かが起きて責任問題へ発展した場合の保険として監査企業は多額の費用を求めます。これは IT コンサルや経営コンサルでも同じことです。このような事例では、公に受発注の事実すら出てこないことが多いため、費用の見積もりは推定になりますが、ペネトレーション・テストの工数を遥かに超える工数が現実に必要となるため、最低でも四桁の後半、アプリケーションの規模によっては億単位の費用になる可能性もあります。WordPress は、いまやウェブ・アプリケーションとしては、さすがに EC サイトなどオンラインでのビジネスを扱うほどの規模ではなくても、個人が開発して公開できるような規模を超えています。したがって、最後の監査方法でも数千万円はかかると思った方がよいでしょう。

WordPress のコア・システムを監査する場合、最後のコンサルティングまでは必要ないと言えるでしょう。確かに『安全なウェブサイトの作り方』には、DNS サーバの管理や SSL サーバ証明書の運用など、ウェブ制作会社が担当しているとは限らない項目も含まれていますし、ポートの開放状況などという項目に至っては root 権限を持っていない場合が多い受託の制作会社には対応不能と言えます。よって、せいぜい自社で実装したテーマやプラグインの挙動についてペネトレーション・テストを実施して品質を保証するていどのことができるにとどまるでしょう。しかし、それでも上記のとおり数百万円では済まない追加費用がかかります。

したがって、まずは WordPress の実装案件について、最低でも以下のような区分を設けて、責任分界を明確にしておくべきでしょう*4。ただし、以下で列挙した区分は、あくまでもウェブサイトの制作・構築を行って納品した時点までの業務という範囲に限られており、サイトを公開した後の定期的な監視やメンテナンスとして必要な業務は含んでいません。今回は『安全なウェブサイトの作り方』の基準に制作会社が制作・構築の業務でどこまで対応できる(すべき)かを議論しているからです。とは言え、監視やメンテナンス業務はウェブサイトのライフサイクルから言えば最も長期にわたって必要となるはずなので、本来はウェブサイトの制作・構築という業務とは別にメンテナンス契約を結ぶなどして体制や手順を決めておいた方が良い筈です*5

*4僕が携わっている受託案件でも、WordPress を使ったサイト制作・構築については、『安全なウェブサイトの作り方』で対策が求められている項目の多くは受託側の制作会社に保証を求められるものではないという理解が発注側の広告代理店さんにも共有されつつあり、WordPress のコア・システムに脆弱性が見つかったという報道が広まった場合でも、トップ・クライアントから瑕疵だと責められるようなことにはなっていません。もちろん、中小零細企業を相手にした直接取引の場面では、こうした WordPress のコア・システムに見つかったような脆弱性を瑕疵だと称して、受託側から賠償だ何だと金銭をせしめてやろうとか、あるいは分割払いにしている制作費の残金支払いを拒否するような「剛腕」経営者や「辣腕」担当者なるチンピラがいてもおかしくありません。そうした金銭にかかわる法的な問題は本稿の範囲を超えますし、ここで何らかの対策を示して責任を負うつもりもありませんので(そもそも中小零細企業や個人事業主を相手にビジネスをするなら、こういうリスクは想定するべきでもあります)、具体的な事案については法律家に相談してください。

*5ただし、このようなメンテナンス契約という名目の「ホームページ詐欺」が横行したこともあったため、発注する側に不安を抱かせないよう工夫する必要はあるでしょう。現在では WordPress のコア・システムは自動アップデートが働くので、自社から納品したオリジナルのコードがアップデートに追随して正しく動作するかどうかをメンテナンスすればいいだけの筈ですから、サイトの規模にもよりますが大した作業でもなければ高額にもならないはずです。

  1. ドメインや SSL サーバ証明書の取得
  2. ウェブ・サーバやデータベース・サーバ(ハウジングか IaaS なら OS のセットアップも含める)
  3. 各サーバのミドルウェア(Apache, Nginx, IIS, MySQL, MariaDB, PHP, あるいは関連するアクセラレータ等)
  4. ネットワーク(特に IaaS でバランサや VPS 接続の特別経路などを導入している場合)
  5. WordPress のコア・システム
  6. /plugins 以下にインストールする、サード・パーティのプラグイン
  7. /themes 以下に実装するテーマ一式
  8. /plugins 以下に実装するプラグイン
  9. それ以外の自社で制作した納品物(WordPress 関係のディレクトリ以外に置く静的ファイルなど)
  10. ウェブサイトで公開するファイルやコンテンツ
  11. ウェブサイトで公開しないファイルやコンテンツ

まず、1. ~ 4. については、WordPress を利用する案件に限った話ではありません。ウェブサイトを構築して納品するのであれば、どういう案件でも必要な管理項目です。大手広告代理店の案件では、コーポレート・サイトだと 1. ~ 4. はトップ・クライアント企業の情報システム部門などが管轄していることが多いので、責任どころか逆に制作会社が全く関与できない事例もあります。『安全なウェブサイトの作り方』のチェックリストには HTTPS プロトコルによる通信を前提にしたチェック項目(たとえば 4-(iii))がありますから、このような項目については管理する責任がどこにあるかを明確にしておきましょう。

あるいは、クラウド・サービスを利用して期間限定のキャンペーンで公開するサイトを構築するといった事案では、ドメインや SSL サーバ証明書の取得・管理からネットワーク管理(機器や帯域の管理というよりも、アクセス数に伴う負荷のモニタリングや、必要に応じた負荷分散や契約内容の更新)にいたるまでを、ウェブサイトの制作側が全て担当することもあります。こういう場合は、それぞれの利用サービスで設定されている「サービスレベル合意書(SLA: Service Level Agreement)」を参考に、受託側がどこまで対応できるかを事前にクライアントと協議しておくべきです。WordPress という CMS についても同様ですが、受託制作側はそれらのサービスを利用しているだけですから、現実には何かが起きたときに対応できる範囲も責任も限られているということを発注側と合意しておく必要があります。負荷が上がってサイトの表示が重くなってきたときに、新しいインスタンスとロード・バランサを作成して負荷を分散させるという作業がスキルとしてできること、それからそういう対応ができる IaaS を利用していること、そして必要に応じて作業をどれくらいの時間で完了できるかという見込みなどを話し合っておいた方がよいでしょう。こういう状況を単純に「トラブル」として描いてしまうと、クライアントにしてみれば面倒な話でしかなくなるわけですが、他方でサーバが重くなるというのは数多くの人たちがサイトにやってきている証拠かもしれないのですから、必ずしも迷惑なことではありません。

次に、WordPress という CMS のコア・システム(5.)を本案件で利用するという採用の責任がどこにあるかを決めておかなくてはなりません。これは、もちろん WordPress を使う案件だけに限った話ではないので、それなりに経験がある広告代理店関係者やディレクターなら既にお判りだと思います。受託側が起案の責任を全て負う場合もありますし、発注側から「こんなサイトを作りたいので、こういう条件で提案してほしい」という提案の要望、あまりウェブ制作の案件では見かけませんが、「提案依頼書(RFP: Request for Proposal)」を出して起案を促したり見積もりを要求している場合は、当然ですが発注側にも与件を漏らさず正確に出す責任があるのですから、WordPress を CMS として採用する責任だけではなく、起案させる際の責任も負います。ともあれ、ウェブサイトの制作・構築において、仕様の最終決裁は発注側にある場合が多いので*6、発注側が何も責任を負わなくていいというわけにはいきません。なお、本稿で言う「コア・システム」とは、オープンソース・ソフトウェアとして WordPress.org から無料でダウンロードできるソフトウェア一式(コミュニティ版)のことです。.zip ファイルとしてダウンロードできます。これらはアーカイブファイルなので、ウェブ・サーバへ展開して利用するのですが、いまでは夥しい数のファイルで構成されています。ウェブサイトのフロント・エンド側(サイトのビジターがアクセスする側)だけでなく、管理システム側のプログラムも含みます。ちなみに、いまでは多くのレンタル・サーバが契約者画面のボタン一つで WordPress を自動インストールするサービスを提供していますが、レンタル・サーバによっては特殊なプラグインが同時にインストールされることがあるので(情報セキュリティ対策用のものが多い)、本稿ではレンタル・サーバで自動インストールされる WordPress は対象外とします。

*6請負契約の場合は必ずしもそうではありません。発注側に全く制作実務の知識やスキルがなくて制作業務を代行してもらうどころか、ウェブサイトが何であるかを知らないとか、自分たちのやりたいことや KPI に照らして受け入れ検査する能力するスキルもない場合は、起案どころかサイト設計の与件まで制作会社が立てなくてはならない場合もあります。

6. の「サード・パーティのプラグイン(発注側でも受託側でもない、それどころか WordPress のコア・システムを開発している Automattic 社でもない者が開発した、WordPress 用のプログラム)」は、そのサイトで何をしたいかというクライアント(あるいはディレクターや IA などウェブサイトの UX を設計している人)の要件に応じて、ウェブサイトを制作したり構築する側が数多くの中から選択しています。たとえば、WordPress の管理システムへログインするときに HTTPS 通信を強制するプラグインが幾つかあります(本来は .htaccess で mod_rewrite の命令を書くだけでも対応できます)。同じ目的に応じて使えるプラグインは複数ありますから、そのどれを使うかは誰かの責任で吟味して選ばなくてはなりません。たいていの場合は、発注企業側にプラグインの是非を検討して決められるようなスキルをもった人物はいないので、受託側が必要かどうかを決めて、使えそうなプラグインを探して、吟味し、提案・採用します。なお、ディレクターの方々に助言として書いておくと、先ほど書いたように、管理システムへのアクセスを HTTPS 通信に強制するなどということは、プラグインなど使わなくても実現できます。また、プラグインを気軽に使っている人たちの中には、PHP で開発できるスキルさえあればどうにでもできることを、わざわざプラグインに頼って実装しているウェブ制作会社もあるわけです*7。したがって、サード・パーティのプラグインについても、発注側としては動作の保証くらいはしてほしいところだと思いますが、現実には自社で動作検証して保証の範囲に含められるウェブ制作会社は非常に少ないと言えます。得てして、プラグインをたくさん使いたがるコーダやプログラマに限ってスキルがないからです。おおよそ、僕が経験してきたキャンペーン・サイトやコーポレート・サイトの制作・構築業務では、WordPress のプラグインなど三つか四つも使えば多い方だと言えます。

*7ということで、たいていのウェブ制作会社のコーダやデザイナーには、プラグインを自力で開発したり、他人が書いたプラグインのコードを検証するようなスキルはありません。というか、PHP を扱える社員が全くいなくても WordPress のサイトを構築しているウェブ制作会社は山ほどあります(彼ら自身がレンタル・サーバで、ワンクリックで WordPress をインストールしているだけだったりするからです)。

したがって、基本的には上記の一覧で 1. ~ 6. は納品時の品質あるいは動作保証など現実にはできるものではないので、メンテナンス契約を交わすのが妥当だと思います。そしてメンテナンスの対応範囲にセキュリティ・インシデントの発生やサーバの負荷上昇等を想定しておいて、WordPress のコア・システムやプラグインの脆弱性が報告された場合やサーバの負荷が上がってきた場合などに、所定の手順で対応をとる約束にしておけばいいでしょう。WordPress をはじめとするオープン・ソースのアプリケーションを使うような事案では、そのアプリケーションの提供元、WordPress なら Automattic 社や、サード・パーティのプラグインの開発者にすら何の品質保証もできないわけなので(それは何も無料だからではありません)、それらをユーザとして利用しているだけの制作会社にアプリケーションの動作品質保証にかかわる契約内容不適合責任があるかのような契約を負わせるのは現実的ではないと言えます。長年にわたってウェブ制作の案件に携わってきた当事者として言わせてもらえば、もし安いというだけで WordPress を提案したり導入するのであれば、手抜きや低予算という理由だけでオープン・ソースのアプリケーションを選択してしまうという愚かさの責任を受発注者が共同で負うべきなのです。

そして、残りの 7. ~ 11. にあたる項目をみましょう。7.(/themes 以下に実装するテーマ一式)については、もちろんディフォールトで入っている幾つかのテーマは除外します。それらのライセンスや著作権や責任は制作会社にありません。ただ、ディフォールトのテーマを使って WordPress のサイトを制作したり構築する場合は、それがいかにディフォールトのテーマであっても、制作会社には採用した責任くらいはあると思います(ナショナル・クライアント案件では殆どありえない状況ですが、個人事業主のサイトを制作する案件ではよくある話です。テーマは出来合いのものを使って、コンテンツだけ制作会社に写真をレタッチしてもらったりページの文章を書いてもらうというわけです)。そして、いったん他人のテーマを使って構築した場合は、テーマもアップデートという措置の対象となる場合がありますから(もちろん、テーマを構成するファイルも PHP として動作しうるからです)それを自動でアップデートさせるに任せるか、それともアップデートで不具合が生じないかどうか、メンテナンス契約を交わして制作会社に検証してもらってからアップデートするか、決めておく方がよいでしょう。もちろん、アップデートしてテーマの表示が崩れたり記事が表示できなくなるような現象は起きていませんが、今後もずっと起きないという保証はありません。

8.(/plugins 以下に実装するプラグイン)は、自社で開発したプラグインを実装するのであれば、情報セキュリティ監査や動作品質の保証範囲とするのは当然のことです。もちろん、WordPress 用のプラグインの大半は WordPress と同じライセンスである GPL によって公開されるのが原則ですから、どういうプラグインのソース・コードでも、それを改変して案件に利用できます(改変したソース・コードをライセンスに従って適正に公表している会社は少ない)。ただし、他人の書いたプログラムを自分で改変して商用のサイトへ実装した場合は、それがいかにオープン・ソースのライセンス(“WITHOUT ANY WARRANTY”)に従うプログラムだとは言え、それを現に書いて実装までした制作会社の責任が、WordPress のコア・システムと同じようになくなるかと言えば、そんなことはありません。当該のウェブサイトを制作・構築するにあたって CMS を使うとか、WordPress を採用するという決裁に受発注双方の責任があるのと同じく、改変しただけのプラグインであろうと、元になるプラグインを選んだり、改変したプラグインを提案し採用した責任は残ります。

残る項目、つまり 9.(それ以外の自社で制作した納品物(WordPress 関係のディレクトリ以外に置くプログラムなど))、10.(ウェブサイトで公開するファイルやコンテンツ)、11.(ウェブサイトで公開しないファイルやコンテンツ)については、色々なファイルが想定できるので、一概にどういう状況で考慮しなければいけないかは難しい判断となります。さしあたって思いついたものを列挙してみると、以下のようになります。

これらについては、もし追加コンテンツがあれば監査の対象になりますし、サイトを制作するのであれば当然ながら多くの静的ファイルもサーバへアップロードして利用すると思うので、それらも監査の対象です(スタイルシートや JavaScript のコードを監査しない事例は非常に多いので困惑しているところですが)。また、アップロードした画像や PDF については、どちらかと言えばそれらのアップロードしたファイルと言うよりも、アップロードする先のディレクトリに設定されているパーミッションや所有権の確認をした方がよいわけですが、ともかく監査の対象になります。そして、テスト時点でアップロードしたファイルをウェブサーバに放置するのはたいていの制作会社では違反行為なので、そういうものは置いてあるものを監査の対象にするということではなく、不要なファイルがないかどうかを監査して、もし放置していれば削除するべきです。もちろん、それ以外に非公開のファイルがサーバに置かれているという正当な事情はたくさんあります。たとえば、サーバのログ・ファイルはたいていのウェブ・サーバで所定のディレクトリに貯まるでしょう(そのディレクトリになくてはいけないのかという点は、もちろん厳密には吟味できます。そのディレクトリが FTP アカウントでもアクセスできるなら、その是非を議論する意味はあります)。また、問い合わせフォームの開発では、問い合わせ内容をサイト運営側へ通知するメールを配信すると同時に、サーバへログ・ファイルにして問い合わせ内容を保存するように開発する場合もあります(あるいはデータベースに登録することもあります)。そうした、非公開のファイルがウェブ・サーバで保存・管理されていることもありますから、どういう仕様によってサイトを制作・構築するかという全体の事情を考慮して、監査の対象を設定しなくてはなりません。そして、そうした受託側の立てた仕様にもとづいてサーバに生成・保存されるファイルについては、その多くを受託側が責任を負うでしょう。(納品してから後は発注側の担当者が WordPress の管理画面から記事を公開したりファイルをアップロードするというのであれば、それらのコンテンツについては、当然ですが発注側が負います。今回は、納品時の監査に絞っているので、納品した後のリスクや責任の所在は議論しません。)

『安全なウェブサイトの作り方』は WordPress の実装案件にどこまで適用できるのか

まず、『安全なウェブサイトの作り方』のチェック・シートに挙がっている各項目について、WordPress のコア・システムやサード・パーティのプラグインといった、ウェブ制作会社の責任分界として設定できない(すべきではない)項目を除外してみます。サイト制作・構築にあたっての与件としては、オリジナルの問い合わせフォームを開発するわけでもない、やや貧弱な構成のコーポレート・サイトとしておきます。ページ数にすれば、だいたい 15 ページ前後の構成でしょう。10年ほど前なら、上場企業のコーポレート・サイトを WordPress で構築するという提案は難しかったのですが、いまでは特に珍しくもない状況となっています。もちろん、その最も大きな理由はサイト制作費の予算が圧縮されていることです。インターネットを利用した広告費は増加していますが、クライアントや広告代理店は広告予算をウェブサイトの制作やコンテンツのクリエーティブになど費やしません。ウェブに配分された広告予算の大半は、芸能プロダクションへのギャランティーか、リスティング広告の支払いなどに使うわけです。

ともあれ、或るコーポレート・サイトを制作するにあたり、受託側の制作会社は以下の条件で作業すると想定しておきます。

SQLインジェクション

1-(i)-a SQL 文の組み立ては全てプレースホルダで実装する。
1-(i)-b SQL 文の組み立てを文字列連結により行う場合は、エスケープ処理等を行うデータベースエンジンの API を用いて、SQL 文のリテラルを正しく構成する。
1-(ii) ウェブ・アプリケーションに渡されるパラメータに SQL 文を直接指定しない。
1-(iii) エラーメッセージをそのままブラウザに表示しない。
1-(iv) データベースアカウントに適切な権限を与える。

1-(i)-a, 1-(i)-b は、プレース・ホルダやキャスト(「型キャスト」という言い方もありますが、本来は型をあてがうことを「キャスト」と言いますし、ソフトウェア開発の分野で型以外に英語なり外来語として「キャスト」と言う事例はないため、本稿では「キャスト」と表記します)等によって対応すべきとされていますが、入力時の対応と SQL としてデータベースへクエリを送出する時の対応、そして結果を受け取った際の対応という三つのポイントを押さえる必要があります。恐らく大多数の事例では、テーマの PHP ファイルから WordPress のテンプレート・タグを使わずに SQL 文のクエリを自力でリクエストして記事やページのデータを拾い上げる状況などないと思いますが、もし自社で開発しているテンプレートで SQL 文を直にコントロールする場合は(第2部で紹介した、「管理画面としての WordPress」という実装方法だと、これがありえます)、当然ですが SQL インジェクションへの対策が求められます。1-(ii) は、要するに hidden 属性の値として SQL 文を送信するという、きわめて愚かなコーディングを想定しているので、こんな事例はないでしょう。1-(iii) は、たいてい .htaccess に php_flag display_errors Off で対応できますが、ウェブ・サーバによっては php_flag が .htaccess で使えない場合があるため(たとえば下記のエラー表示が示すように、CPI (KDDI) を始めとするレンタルサーバではミドルウェアの挙動やリソースを制御する設定は抑制されていることが多いです)、他の手法を検討する必要もあります。

WordPress

1-(iv) については、記事を書くていどの更新しかしない場合は privileges を SELECT, INSERT, UPDATE, DELETE だけに権限を絞ることが望ましいでしょう。しかし、データベースのユーザ権限が最初から設定されてしまっているレンタル・サーバもありますし、権限を変更できないユーザとしてアカウントがクライアント側から発行される場合もあるでしょう。また、レンタル・サーバなどの場合はデータベース・サーバへ直に ssh などでアクセスできなくなっていることも多いため、phpMyAdmin のような管理ツールを使って間接的にアクセスすることでアカウントの privileges を変更できれば上出来かもしれません。

OS コマンド・インジェクション対策

2-(i) シェルを起動できる言語機能の利用を避ける。
2-(ii) シェルを起動できる言語機能を利用する場合は、その引数を構成する全ての変数に対してチェックを行い、あらかじめ許可した処理のみを実行する。

2-(i) については、WordPress コア・システムや、サード・パーティのプラグインがシェル・コマンドを呼び出す関数を使っているかどうかを確認してみる必要があります。自分で使っていなければ問題ありませんし、ローカルでもテストはできるでしょう。なお、本稿で想定する責任分界から外れますが、ウェブ・サーバの管理も担当している状況であれば、該当する関数を使っていないと分かっても、php.ini を制御できる権限によって、disable_functions = "eval, exec, shell_exec, system" などと設定しておくと、将来の脆弱性(何か新しいコードが導入されたり、サード・パーティなどのアップデートで入り込んでしまった新しい問題)を利用されて、勝手に危険な関数を使われるリスクは低減できるでしょう。

そして 2-(ii) に該当するコードについては、個々の関数名でソース・コード全体を検索して該当箇所を検査してもよいでしょう。ただし、これはペネトレーション・テストの範疇にも入るため(外部からの入力値を使う場合)、素人が適当な攻撃コードを幾つかコピペして試したというだけで保証になると思ったら大きな勘違いです。

パス名パラメータの未チェック/ディレクトリ・トラバーサル

3-(i)-a 外部からのパラメータでウェブサーバ内のファイル名を直接指定する実装を避ける。
3-(i)-b ファイルを開く際は、固定のディレクトリを指定し、かつファイル名にディレクトリ名が含まれないようにする。
3-(ii) ウェブサーバ内のファイルへのアクセス権限の設定を正しく管理する。
3-(iii) ファイル名のチェックを行う。

3-(i)-a, 3-(i)-b については、自社で開発しているテーマのテンプレートなどに書いた PHP で、危険な文字列の扱いをしていないかどうかを調べましょう。3-(ii) については、WordPress のドキュメントでファイルのパーミッションの変更を参照してください。これはコア・システムか自社の開発かに関係なく、ファイルをウェブ・サーバへアップロードする権限さえ与えられていれば、通常は受託側が安全なパーミッションを設定できます。特に permalink を設定したとき(httpd の mod_rewrite モジュール等)は .htaccess に一時的な書き込み可能の属性を付けたりするので、設定を終えたらパーミッションをもとに戻さなくてはなりません。また、案件によってはプラグインを全く使わない場合もあるので、そういう状況なら /wp-content/plugins ディレクトリに書き込み属性を付ける必要はありません(更に DISALLOW_FILE_EDIT などを設定して管理画面上でのファイル編集を禁止している場合は、ディレクトリの属性をもっと制限してもよいでしょう)。最後の 3-(iii) については、ディレクトリ・セパレータとして解釈しうる文字列を見つけたら処理を停止しましょう。URL などを入力できるフォームでは、プロトコルからの記述を必須とします。

セッション管理の不備

4-(i) セッション ID を推測が困難なものにする。
4-(ii) セッション ID を URL パラメータに格納しない。
4-(iii) HTTPS 通信で利用する Cookie には secure 属性を加える。
4-(iv)-a ログイン成功後に、新しくセッションを開始する。
4-(iv)-b ログイン成功後に、既存のセッション ID とは別に秘密情報を発行し、ページの遷移ごとにその値を確認する。
4-(v) セッション ID を固定値にしない。
4-(vi) セッション ID を Cookie にセットする場合、有効期限の設定に注意する。

まず最初にはっきりさせておくと、WordPress のコア・システムはセッションを使っていません。Cookie は使っているので、ブラウザで確認できます。したがって、セッション管理についての項目は該当しないものもあります。もしテーマや自作のプラグインで PHP のセッションを使うなら、セッション管理は use_strict_session, use_strict_mode を有効にするといった対策を使わないと効果的な対策は望めません。.htaccess で設定可能なディレクティブは採用した方がいいでしょう。それから、4-(ii) については、認証フローにおいて ?action=logout&_wpnonce=19a7ca21d2 のように nonce を使っています。4-(iii) についても、.htaccess で session.cookie_secure を有効にします。“Any type of connection” として HTTP でも許容している Cookie があるため、これは一律に有効としても問題はありません。

4-(iv)-a については、何かデータを書き込んだり更新するタイミングで再生成します。session.use_strict_mode が有効なら、すぐに古いセッション ID は破棄されるので、特別な追加の対策は不要です。4-(iv)-b については、管理画面の仕様としてトークン(WordPress の用語では “nonce”)を使っています。wp-config.php に “NONCE_KEY” などの値を個別に生成してちゃんと設定している人は知ってるはずですね。

4-(v) は、コア・システムの内部でどういう取り回しなのかは分かりませんが、nonce というクエリ文字列として使いまわされている値は、ブラウザを再起動して管理システムへログインしなおすと違う値になっているため、wp-config.php で設定されている値を常に初期値の一部として使っている可能性はあるのですが、毎回同じ値で固定しているわけではないと推定できます。ただ、いずれにしても制作会社がどうにかできる範囲に限ると、テーマや自作のプラグインなどでの対応に限られるでしょう。そして最後に 4-(vi) は、実用的な範囲で可能な限り短く設定したいところです。ただ、記事をオン書きで作成する場合もあるでしょうから、session.cookie_lifetime 0(ブラウザを閉じたら無効)が妥当な設定なのかもしれません。

クロスサイト・スクリプティング

5-(i) ウェブページに出力する全ての要素に対して、エスケープ処理を施す。
5-(ii) URL を出力するときは、「http://」や 「https://」で始まる URL のみを許可する。
5-(iii) <script>...</script> 要素の内容を動的に生成しない。
5-(iv) スタイルシートを任意のサイトから取り込めるようにしない。
5-(v) 入力値の内容チェックを行う。
5-(vi) 入力された HTML テキストから構文解析木を作成し、スクリプトを含まない必要な要素のみを抽出する。
5-(vii) 入力された HTML テキストから、スクリプトに該当する文字列を排除する。
5-(viii) HTTP レスポンスヘッダの Content-Type フィールドに文字コード(charset)を指定する。
5-(ix) Cookie 情報の漏えい対策として、発行する Cookie に HttpOnly 属性を加え、TRACE メソッドを無効化する。
5-(x) クロスサイト・スクリプティングの潜在的な脆弱性対策として有効なブラウザの機能を有効にするレスポンスヘッダを返す。

5-(i) から 5-(iii) は、もちろん自力で組んでいるスクリプトにおいては必須の処置ですが、コア・システムや他人が書いたプラグインで適用している対策かどうかは分かりません。5-(iv) については、expression のように CSS で JavaScript を実行するなどという危険な設計を採用する方がどうかしています。ふつうは JavaScript の方で CSS プロパティを制御するのが当たり前でしょう。メンテナンス性も低下します。おまけに、expression は IE7 までの独自仕様であって、IE8 以降では動作しませんから、既にこんなものを使っている制作会社は殆どないと思います。

5-(v) は、俗に言う「サニタイズ」処理です。これも、たとえばフォームの値であれば、HTML の input タグにおける maxlength などの抑制、JavaScript によるエラー判定、そして最後に PHP でのバリデーションという3段階の処理が考えられます(httpd で GET や POST のペイロードを制限することもできますが)。こんなことは当たり前の処置だと思いますので、当然ながら自社で納品するコードでは実装しているべきです。

5-(vi) については、分かりにくい表現ですが、要するにタグを含む入力テキスト(例えば「<a href="https://www.yahoo.co.jp/">いんたーねっつ</a>」)を許容する場合にバリデーションしろということです。WordPress は、もちろんタグも含めて扱えないといけないので、これをまともに対策していなければ CMS として問題外だと思いますから、何らかの対策はコア・システムでもやっているはずです。ただし、既に上記でも指摘していますが、コメントにタグが書いてあると管理画面でコメントの一覧を表示したときに、コメントに書かれているタグをディフォールトでプレビューしてしまう仕様だったため、<script> タグを使えば簡単に JavaScript を管理画面で実行させられるという酷い状態でした。ともかく、自社で実装しているコードでタグを含めた入力値を扱うのであれば、それなりに正規表現に強いプログラマや、ペネトレーション・テストもできるていどのスキルがある人にバリデーションの設計は任せた方がよいでしょう。

5-(vii) については、僕が所属する企業では、タグを含むような文字列を扱わないという前提があれば htmlspecialchars を使っています。他にも、以下のような条件にマッチする文字列は単純に捨てていますので、こういう文字列を入力して困るような人は勝手に困ってくださいという方針です。

\s*<\s*s\s*c\s*r\s*i\s*p\s*t \s*&lt;\s*s\s*c\s*r\s*i\s*p\s*t \s*%3C\s*s\s*c\s*r\s*i\s*p\s*t 0x3C\s*0x73\s*0x63\s*0x72\s*0x69\s*0x70\s*0x74 U+003C\s*U+0073\s*U+0063\s*U+0072\s*U+0069\s*U+0070\s*U+0074 003C 0073 0063 0072 0069 0070 0074 0x3C 0x53 0x43 0x52 0x49 0x50 0x54 U+003C\s*U+0053\s*U+0043\s*U+0052\s*U+0049\s*U+0050\s*U+0054 003C 0053 0043 0052 0049 0050 0054 %3C%73%63%72%69%70%74 &#x3C;&#x73;&#x63;&#x72;&#x69;&#x70;&#x74; &#60&#115&#99&#114&#105&#112&#116 PHNjcmlwdA== %3C%53%43%52%49%50%54 &#x3C;&#x53;&#x43;&#x52;&#x49;&#x50;&#x54; &#60&#83&#67&#82&#73&#80&#84 PFNDUklQVA==

5-(viii) については、表示しているコンテンツには適切なヘッダを指定しましょうという、それだけのことです。自作のスクリプトで HTML をレスポンスする場合には、当然ですが Content-Type を先行して出力するべきです(手作業で HTML ページを書いていた人が多かった時代は、エンコーディングなど無視して iso-8859-2 が当たり前だという傲慢なアメリカ人も多かったわけですが、いまどきブラウザのエンコーディング判定に依存してメタ情報を無視したページなど殆どないでしょう)。

5-(ix) は、php_flag session.cookie_httponly 1、そして mod_rewrite のような、環境変数やリクエスト内容を判定できるモジュールを使っている場合は、TRACE どころか次の全てを無視するべきです。HEAD, TRACE, DELETE, TRACK, PROPFIND, OPTIONS, PROPPATCH, REPORT, MKACTIVITY, CHECKOUT, PUT, MERGE, CONNECT, PATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, VERSION_CONTROL, UNCHECKOUT, CHECKIN, UPDATE, LABEL, MKWORKSPACE, BASELINE_CONTROL, INVALID など、調べればたくさんあります。ブラックリストでは未知のメソッドに対応できないので、この場合は GET, POST などを指定するホワイトリストで対応しましょう。

そして 5-(x) では、X-Content-Type-Options:nosniffX-XSS-Protection:1; mode=block は必須で、フレームを使っていないなら X-Frame-Options:DENY も追加できます。また、.htaccess では CSP として幾つかのヘッダーを使っています。外部 API や Google 関連の JavaScript コードを全く使っていないのであれば、X-Content-Security-Policy "default-src 'self';" としてしまってもいいでしょう。

CSRF(クロスサイト・リクエスト・フォージェリ)

6-(i)-a 処理を実行するページを POST メソッドでアクセスするようにし、その「hidden パラメータ」に秘密情報が挿入されるよう、前のページを自動生成して、実行ページではその値が正しい場合のみ処理を実行する。
6-(i)-b 処理を実行する直前のページで再度パスワードの入力を求め、実行ページでは、再度入力されたパスワードが正しい場合のみ処理を実行する。
6-(i)-c Referer が正しいリンク元かを確認し、正しい場合のみ処理を実行する。
6-(ii) 重要な操作を行った際に、その旨を登録済みのメールアドレスに自動送信する。

これらの対策のうち、WordPress のコア・システムが対応していない項目については、管理画面を改変するところまで受託側が作業する想定で議論していないので、本稿では扱いません。テーマを構成する THEMEDIR/index.php のようなファイルで、上記のような対策を施しても不十分であり、コア・システムに不備があれば不徹底でもあります。ただし、サイトの制作で導入したサード・パーティのプラグインでフォームを利用する場合は、そのプラグインが上記の対策をしているかどうか検証する必要はあるでしょう。フォームへのアクセスが正しいリンク元であるかどうかを確認したり(6-(i)-c)、フォームの送信時に外部から勝手に送られてきたデータではないことを保証するために(上記では「パスワード」とか「秘密情報」となっていますが)token 値を要求したり(6-(i)-a, 6-(i)-b)といった対策です。

HTTP ヘッダ・インジェクション

7-(i)-a ヘッダの出力を直接行わず、ウェブ・アプリケーションの実行環境や言語に用意されているヘッダ出力用 API を使用する。
7-(i)-b 改行コードを適切に処理するヘッダ出力用 API を利用できない場合は、改行を許可しないよう、開発者自身で適切な処理を実装する。
7-(ii) 外部からの入力の全てについて、改行コードを削除する。

HTTP レスポンスのヘッダ値を外部から得たデータを元に作るという仕様は、そういう設計自体が危険だと言えます。本当にそんなことをする必要があるのかどうかを冷静に検討してから設計する必要があることは言うまでもありません。そして、もし必要だと判断したときでも、ここでは情報セキュリティの話をしているので当たり前のことですが、いわゆる “security by design” の発想を取り入れて、そのサイトやルーチンで何をしたいのかだけを考えるのではなく、それを安全に(利用者も運営者もウェブサイトの運営に関わるステークホルダです)実現するにはどうあるべきかを考えなくてはなりません。単に動けばいいというなら、「情報」の期末試験を終えた高校生にでもコーディングさせたらいいでしょう(恐らく高校の単元ではまともな情報セキュリティの知識など教えていない筈ですが、僕らのようなプロの技術者よりも巧みにコーディングできるだけの子供はたくさんいるはずです)。

多くの企業で WordPress を採用したサイトを制作する場合には、記事やページにコメントを受け付けるような機能は実装しない筈ですから、上記のような対策が必要となるのは、ほぼ問い合わせフォームのような事例に限られると思います。このセクションで重要なのは、正規のヘッダ値に加えて追加されたり差し込まれた不正なヘッダ値をもたせないよう、徹底して改行コードを取り除くことにあります。もちろん、フォームで「お問い合わせ内容」などとして入力してもらう文章には改行が含まれることがありますし、フォームを受け付ける側でも改行されていなければ読み辛いのは確かですから、フォームの値として改行コードを扱わなくてはならない場合があるでしょう。しかし、(1) ウェブ・ページとして見える改行、(2) 改行コード、(3) 改行を意味する文字列、という三つを区別して考えておけば、それらを必要に応じて置き換えられます。たとえば、当サイトで僕が書いている「落書き(Notes)」の文章には段落がありますが、これらはデータとして格納しているテキスト・ファイルでは HTML のタグでもなければ改行文字ですらなく、改行を表す文字列として扱っている「*****」という文字列です。つまり、落書きで掲載している文章は、<p> ... </p> で囲まれた中に展開されるようになっていて、「*****」が現れると </p>\n<p> という文字列に置き換わって、HTML としては <p> タグの要素として段落が出力されるようになっているわけです。したがって、改行を表すタグどころか改行記号すら使わずに「改行」をウェブ・ページで presentation できればいいという設計になっています。

メールヘッダ・インジェクション

8-(i)-a メールヘッダを固定値にして、外部からの入力はすべてメール本文に出力する。
8-(i)-b メールヘッダを固定値にできない場合、ウェブアプリケーションの実行環境や言語に用意されているメール送信用 API を使用する。
8-(ii) HTML で宛先を指定しない。
8-(iii) 外部からの入力の全てについて、改行コードを削除する。

これも、ほぼ問い合わせフォームで受け付けた内容を元に、運営者がメールを受信するような仕組みをサイトへ組み込む場合に必要な処置と考えておけばよいでしょう。WordPress のコア・システムが管理目的で送信するメールについては、ここでは扱いません。すると、まず 8-(iii) は既に議論したので割愛するとして、それ以外について議論しましょう。8-(ii) は『安全なウェブサイトの作り方』で解説されているように、普通の設計ではありえないことなので、寧ろみなさんが新人研修のネタとして使えばいいような話でしょう。<input type="hidden" name="admin_email" value="admin@company.co.jp"> などとメールの宛先を HTML のタグとしてわざわざ書くような人がいたら、注意してあげましょう。そして、8-(i)-a, 8-(i)-b ではメールのヘッダを固定するように推奨されています。事例としては、以下のとおりです。

mb_language( 'japanese' ); $subject = 'Inquiry'; $to = "inquiry_on_hold@company.co.jp"; $headers = ''; $headers .= "From: inquiry_on_hold@company.co.jp" . "\n"; $text = '本文がここに入ります'; $body = mb_convert_encoding( $text, 'ISO-2022-JP', 'AUTO' ); mb_send_mail( $to, mb_convert_encoding( $subject, 'utf-8', 'auto' ), $body, $headers );

ちなみに大半のメール・ソフトでは本文も件名も ISO-2022-JP でエンコーディングすれば正しく表示されますが、Mozilla がリリースしている Thunderbird というメール・ソフトは、もう何年も前から機会があるたびに僕は仕様バグだと指摘しているわけですが、意味もなく件名が UTF-8 で固定されているらしいので、こういうバッド・ノウハウが必要となります。逆に、本文と件名で異なるエンコーディングでも対応できている他のメール・ソフトの方に敬意を払いたいくらいです。

クリックジャッキング

9-(i)-a HTTPレスポンスヘッダに、X-Frame-Options ヘッダフィールドを出力し、他ドメインのサイトからの frame 要素や iframe 要素による読み込みを制限する。
9-(i)-b 処理を実行する直前のページで再度パスワードの入力を求め、実行ページでは、再度入力されたパスワードが正しい場合のみ処理を実行する。
9-(ii) 重要な処理は、一連の操作をマウスのみで実行できないようにする。

以上の対策は、既に他の項目でも CSP (Content Security Policy) のヘッダでフレームを抑制する話としてもご紹介しましたので、繰り返しません。また、これらの対策が必要なのは WordPress のコア・システムであって、テーマを制作するだけの事案であれば、逆に対策のやりようがないとも言えます。

バッファオーバーフロー

10-(i)-a 直接メモリにアクセスできない言語で記述する。
10-(i)-b 直接メモリにアクセスできる言語で記述する部分を最小限にする。
10-(ii) 脆弱性が修正されたバージョンのライブラリを使用する。

WordPress でサイトを制作する場合に C や C++ といったメモリ制御が可能な言語を直に使う機会はないと思いますが、C や C++ で書かれたプログラムを PHP のスクリプトから呼び出したりアクセスすることはありえます。したがって、どうしてそういう設計が必要なのかという設計の段階から、ここでも “security by design” のスタンスで判断することが望ましいでしょう。もちろん、必要に応じて CGI で C のプログラムを実行するのであれば、それに見合った上記のようなセキュリティ対策は必要です。ただ、「最小限」と言っても曖昧ですから、ここでも必要と判断できるときにメモリを制御することが望ましいと言えます。まぁ、大多数の場合には WordPress のサイトで C のプログラムを動かすなんてないと思いますが。

アクセス制御や認可制御の欠落

11-(i) アクセス制御機能による防御措置が必要とされるウェブサイトには、パスワード等の秘密情報の入力を必要とする認証機能を設ける。
11-(ii) 認証機能に加えて認可制御の処理を実装し、ログイン中の利用者が他人になりすましてアクセスできないようにする。

以上の点については、WordPress にはユーザ権限の設定がありますから、特に心配する必要はないでしょう。実務としては、受託案件として WordPress でサイトを制作しているときには、われわれが administrator 権限のユーザとして WordPress を扱い、必要に応じてプラグインをインストールして設定します。そして、クライアントの担当者はたいてい記事を書いたり画像をアップロードするだけですから、権限を更に弱くしたユーザを作成して、納品時には両方のユーザ情報を知らせてパスワードは変更してもらうようにお願いしています(もちろん、僕らが知ってるパスワードから変更してもらうためです。逆に、こうしないと手離れの悪い客がイージーに修正だ追加だと言ってくるので、それを抑制するためでもあります)。更に、WordPress の管理画面でメニューの表示を変更できるプラグインがあるので、記事を公開したり編集するだけのユーザとしてログインしたときは設定や外観やコメント(企業案件では、コメントは受け付けないので不要という場合が大半です)といったメニューを隠していたりします。

冒頭に戻る


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

Twitter Facebook