WordPress のセキュリティ対策(補遺)

河本孝之(Takayuki Kawamoto)

Contact: takayuki.kawamoto (at) gmail.com.

ORCID iD iconORCID, Google Scholar, PhilPapers, about.me.

First published: 2017-03-03 17:18:47,
Modified: 2017-03-08 17:25:22, 2017-03-11 09:22:10, 2017-09-05 12:04:07, 2019-01-24 16:52:58, 2019-02-13 17:06:33
Last modified: 2019-02-19 17:39:35.

はじめに

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

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

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

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

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

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

冒頭に戻る

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

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

具体的に言うと、WordPress が出力しているページのソースをブラウザで表示すると、

<meta name="generator" content="WordPress 4.7.3" />

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

しかし現在のユースケース、技術的な可能性、それから現実に起きているインシデントで使われている手法がどれくらい簡単に入手して使えるかを考慮すると、バージョン表記を抑制するていどのことでリスクが実質的に低減した時代は既に終わっていると考えるのが妥当です。その最も大きな根拠は、WordPress で構築されたウェブサイトに対する攻撃の多くは大量のボットで構成されたボットネットで自動化された、無差別かつ大規模な手法を使っているというポイントにあります。いまや、攻撃する側は WordPress のバージョンが幾つなのかとか、それがページに表示されるかどうかなどということは気にしません。

つまり、実質として WordPress のバージョンが当該の脆弱性について対策できていなければいけないという、ごく当たり前の事実だけが残るわけです(最初は「最新でなければならない」と書いていましたが、もちろん最新のバージョンにしているからといって安全だとは限りません。せいぜい「既知の脆弱性について一定のセキュリティレベルの対策を施した」と言えるだけです)。バージョンの表示を抑制するなどというハッタリがセキュリティ対策になるなどという牧歌的な時代は終わりました。

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

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

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

社内に、この程度の「セキュリティ対策」とやらを検索して見つけてくるくらいの従業員はいても、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 とファイルシステムに対して正常に動作すれば十分です。テーマファイルは必要最低限(というか実質としては不要)でよいため、ディフォールトのテーマである Twenty(****) を選択し、それ以外のテーマは全てサーバから削除します。ウェブサイトのセキュリティを維持する些細でありながらも確実なコツの一つは、「使わないファイルを放置しない」(少なくともウェブサーバの公開領域に放置しない)ということです。残念ながら、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 アドレスからのアクセスだけを許可したり、ベーシック認証を設定するなどの処置が望ましいです。ただし、この手法は SSL サーバ証明書が公開サイトと管理サイトの二つに必要なので、予算に一定の余裕が必要でしょう(昨今はメール認証やドメイン認証で取得できる証明書なら年間でも 3,000 円以下で取得できますが、レンタルサーバで好き勝手な SSL サーバ証明書をインストールできるところはありません。やはり専有サーバか VPS を借りるのが使い勝手は良くなります。自分でサーバを運用できないなら、「マネージド」と呼ばれるオプションがついたプランのサーバを選べばよいでしょう)。こうして管理サイトを独立させると、公開サイトには WordPress に関わるファイルが一つもなくなるため、フロントエンド側の作業は非常に見通しがよくなります。

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

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

冒頭に戻る

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

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

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

ところが、/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 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' ); ?> という文字列だけであって、データベースの接続情報などは保護されます*5。“/var/www/lib/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. [G] wp-config.php の安全性を高める
  2. [G] WordPress の管理画面に対してアクセス制限を行う
  3. [B] 接続元の IP によるサーバーへのアクセス制限
  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 のパスが何種類であろうと決まってしまっていれば、仮にシェルコマンドすら実行できてしまうようなリスクがあるときに、決まった場所にあるファイルへ自動で実行されたプログラムがしらみつぶしにアクセスするだけで 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編 後編~」のような記事は、実際にサイトのセキュリティレベルを監査したり検証する側からの議論として興味深い内容があるので、一読をお勧めします。それこそ、「ワードプレス」とカタカナで書いてあるような素人のコピペ記事を 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 のディレクトリ名を変更することは簡単です。

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

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

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

冒頭に戻る


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

Twitter Facebook