まったり技術ブログ

主にWebエンジニア向けのセキュリティブログ。たまに開発も

WordPressプラグイン開発のセキュリティ

f:id:motikan2010:20201117002745p:plain:w500

はじめに

 WordPress プグラインを開発するにあたって、脆弱性を作らないためにはどのような点に気を付ける必要があるのかを紹介していきます。

★ 以下の方を対象に本記事を書いています。

  • WordPress プラグイン開発者
  • WordPress プラグインの脆弱性を見つけたい方
  • 脆弱な WordPress プラグインを開発したい方

★ 本記事では以下の脆弱性6つについて紹介していきます。

  1. PHPファイルへの直接アクセス
  2. サードパーティライブラリ
  3. 権限の検証に不備があるAPIの呼び出し
  4. CSRF(Cross-site Request Forgery)
  5. XSS(Cross-site scripting)
  6. SQL インジェクション

 また、WPScan から「WordPress Plugin Security Testing Cheat Sheet」というセキュリティテストのチートシートが提供されております。

こちらは説明が少し物足りないように見受けられますが、網羅的に脆弱性が紹介されていますので、目を通しておくことをオススメします。

github.com

脆弱性種類

 WordPress 色が強い脆弱性順で紹介しています。

1. PHPファイルへの直接アクセス

 まず WordPress のドキュメントルート以下には誰でもアクセスすることが可能です。

 プラグインのインストール先である /wp-content/plugins 以下も例外ではないため、プラグインに含まれるPHPファイルに WordPress 経由でなくともアクセスすることが可能です。

 そのため WordPress 経由でのコード実行を想定している場合に、直接アクセスされることで意図していない処理が行われる恐れがあります。

対策

 直接PHPファイルが呼び出された場合にはエラーを返すようにし、WordPress コード内から呼び出された場合にのみ処理を始めるようにします。

具体的にはPHPファイルの先頭に以下のコードを記述します。

<?php

if ( !defined('ABSPATH') ) {
    die( 'Forbidden' );
}

 定数「ABSPATH」は wp-config.php で定義されることになっているので、WordPress 経由でないと「if より後続の処理が実行されない」ということになります。
直接PHPファイルにアクセスされても処理が行われないようになります。

機密ファイルへの直接アクセス

 似たような特徴を持った脆弱性を1つ紹介します。

 前述しましたがプラグインのディレクトリ配下は誰もがアクセスすることが可能です。

 そのディレクトリにアクセスログや管理操作ログなどの機密情報を保存するプラグインの場合、それらのファイルにアクセスされ、内容が閲覧されてしまう可能性があります。実際にそのような脆弱性が報告されているプラグインも存在しています。

 そこで機密情報を含んだファイルを扱っているプラグインは以下のような実装をすることで内容が閲覧されないように対策されていました。

  1. ファイル名に推測されないランダム文字列の利用
    → ファイルにアクセスすることが難しくなる
  2. ファイル内容の先頭に<?php exit; ?> と記述し、ファイル名を *.php で保存
    → ファイルにアクセスされても内容が表示されない
    f:id:motikan2010:20201117211421p:plain:w500
    このようにすると秘密の情報が読み取られません

 上の対策方法の片方を行えば問題ないと考えられますが、両方やっていた方がより安全かなと思っていたりします。
何かの拍子に 「ディレクトリリスティングが有効状態」や「PHPが実行されずPHPファイルがダウンロードされる状態」になった場合のことを考えて。

2. サードパーティライブラリ

 サードパーティから提供されているプログラムを用いて WordPress プラグインを開発した場合に発生する可能性がある脆弱性です。

事例

 2つ事例を紹介します。

事例1 : elFinder

 「File Manager」には /wp-content/plugins/wp-file-manager/lib/php/connector.minimal.php に特定のリクエストを送信することで任意のファイルをアップロードすることができるという脆弱性がありました。

 これはライブラリ(elFinder)が WordPress を前提で開発されているものではなく、それを考慮せずにライブラリをディレクトリ配下に配置したのが原因で脆弱性が生じてしまったと考えられます。

File Manager < 6.9 - Unauthenticated Arbitrary File Upload leading to RCE

 サードパーティライブラリを導入することで脆弱性が生じてしまうのは、ライブラリの導入方法が同様であれば脆弱性も同様に発生する点が特徴です。

 実際に「augmented-reality(脆弱性が修正されず非公開状態)」という WordPress プラグインにも全く同じ脆弱性が発見されています。
攻撃者は、このライブラリを用いているプラグインが他に存在するか確認していることでしょう。

Augmented Reality <= 1.2.0 - Unauthenticated PHP File Upload leading to RCE

事例2 : Epsilon Framework

 こちらの事例はプラグインではなくテーマの脆弱性です。

しかし脆弱性が生じた原因は事例1と大きく変わらないです。

 Epsilon Framework を利用している15種のテーマから脆弱性が発見されたというものです。

blog.nintechnet.com

 これらのことからサードパーティライブラリを利用して WordPress プラグインを開発する場合は、ライブラリ導入時に直接アクセスされたくないファイルは「除外」または「 WordPress 経由以外ではコードを実行させないように修正」したほうが良さそうです。
(例:デバッグ用のコードが含まれていそうなテストコード tests/ 配下を削除)

 しかしながらサードパーティーライブラリの中身を見てファイルの必要有無の判断やコードの修正するのは難しいです。

日々のプラグイン脆弱性情報から開発で利用しているライブラリ経由で発生している脆弱性がないかを確認するようにし、該当するようであればパッチを当てる運用でも問題ないと思われます。

3. 権限の検証に不備があるAPI

 WordPress プラグインは register_rest_route 関数 を用いることで、容易に WordPress サイトに REST API を実装することが可能です。

 その API に権限の検証に不備があったり、そもそも権限の検証をしていないといったことが脆弱性になります。

 脆弱性が悪用されることでプラグインの機能が意図していない第三者によって利用される恐れがあります。(記事の投稿・ユーザアカウントの作成 など)

REST API ルートエンドポイント

 ちなみにサイトにどのような REST API の定義情報は、REST API ルートエンドポイント(?rest_route=/)にアクセスすることで確認できるようになっています。

この機能はデフォルトで有効になっています。無効にするには、WordPressのコードを修正するか、この機能を無効化するプラグインを導入する必要があります。

 この定義情報は攻撃者にとっては有意義な情報であり無効状態が推奨されますが、普段使う機能でないためにデフォルト(有効)状態なっているサイトが多く見られます。

f:id:motikan2010:20201116204043p:plain:w500
ルートエンドポイントにアクセスすると定義されているAPI一覧が表示されます

権限

 REST API のルートエンドポイントから API の定義情報からインストールされているプラグインが確認できることから、プラグインの REST API が攻撃の対象となってしまうことがあります。

 そのため REST API にアクセスされた場合に権限の検証をすることは必須事項です。

 WordPress では権限を検証するために current_user_can 関数が用意されており、簡単に権限の検証を行えるようになっています。

 current_user_can 関数の詳しい使い方は以下を参照下さい。
current_user_can – WordPress私的マニュアル

 むやみにcurrent_user_can 関数を使えば安全というわけではなく、引数として渡す権限のレベルにも気を付けましょう。 寄稿者アカウントで管理者アカウント用の操作ができるようであれば安全とは言えません。

 権限のレベルについては以下を参照下さい。
LevelとCapability – WordPress私的マニュアル

事例

 権限検証の不備で脆弱性が発生してしますが、current_user_can関数を用いることで脆弱性の修正が行われています。

blog.nintechnet.com

f:id:motikan2010:20201116212145p:plain:w500
権限の不備が起因となった脆弱性の修正内容

4. CSRF(Cross-site Request Forgery)

 特定のWebアプリケーションフレームワークを用いて開発している場合は自動的にCSRFトークンの検証が行われますが、WordPress の場合は自動的にはCSRFの検証は行われません

 CSRFトークンの検証はプラグイン開発者自身で実装する必要があります。
実装といってもトークンの検証する wp_verify_nonce 関数が用意されてますので、簡単に実装することができます。

 wp_verify_nonce 関数を用いてトークンの検証する場合に気を付けることがあります。

 以下の記事は wp_verify_nonce 関数を用いているにも関わらず 25種のテーマで CSRF の脆弱性が発見されたというものです。

blog.nintechnet.com

 1つのパターンを引用し説明します。
とあるプラグイン では以下のコードで CSRF 対策が行われているようです。

if ( isset( $_POST['some-nonce'] ) && ! wp_verify_nonce( $_POST['some-nonce'], 'some-nonce' ) ) {
   exit( 'Potential CSRF attack detected.' );
}

 some-nonceパラメータが CSRF トークンであり、wp_verify_nonce関数を用いてCSRFの検証を行い、検証結果が false の場合に処理を中断するので CSRF 対策ができていそうです。

 しかしこのコードでは some-nonce パラメータが送信されない場合が考慮されていないようです。

some-nonce パラメータが送信されていない場合は isset 部分で false となりトークンの検証が行われずに後続処理が行われてしまいます。

 トークンが送信されない場合はエラーとしたいので最初の条件は ! isset( $_REQUEST['some-nonce'] ) にする必要があります。

細かい点ではありますが、実際にいくつかのテーマでこのような実装が発見されているので、このようなパターンがあることが頭に置いていた方がよさそうです。

 このような小さなミスで脆弱性が発生するので、脆弱性を見つけてみたいという方には上の記事は面白いと思います。

5. XSS(Cross-site scripting)

 ユーザの入力値を返すようなプラグインに発生する可能性がある脆弱性です。

 Webアプリケーションフレームワークを用いて開発した場合、ブラウザへの出力する際に自動的にエスケープ(サニタイジング)されるのがほとんどですが、WordPress プラグイン開発では自動的にエスケープされません

 WordPress ライブラリ開発ではエスケープ漏れの部分があると XSS の脆弱性が発生する可能性があります。

 PHPではhtmlspecialchars関数を用いてエスケープすることができますが、WordPress には表示箇所に応じたエスケープ処理をラップした関数が用意されていますので、それらを用いて開発した方が無難です。

関数名 表示箇所
esc_html タグ要素内
esc_attr タグ属性内
esc_url href属性内(利用できるスキームに制限が掛かるようになります)
esc_js script 内

6. SQLインジェクション

 読み込み、書き込み問わずデータベースにアクセスする必要があるプラグインに発生する可能性がある脆弱性です。

 WordPress にはデフォルトで O/Rマッパー が用意されていないので、SQLインジェクションの脆弱性が発見されることがたびたびあります。

対策

prepare 関数 で対策

 WordPress はデフォルトで O/Rマッパー を用意されておらず、データベースにアクセスするためには 素のSQL文 をコード内に記述する必要があります。

 セキュリティを意識せずにSQL文を組み立てることで、ユーザが入力した任意の値がSQL文の中に挿入されてしまいます。

 ユーザから入力値をエスケープするために prepare関数 を用いるようにしましょう。

prepare関数 の使い方は以下のページが参考になります。
関数リファレンス/wpdb Class - WordPress Codex 日本語版

 以下の差分はとあるプラグインの SQLインジェクション の修正コミットです。

SQLインジェクションの修正部分
SQLインジェクションの修正部分

バリデーション で対策

 POST(投稿) ID のような数値が想定されている値については、ユーザからの入力値を int型にキャストする処理をします。
また 数値であることの検証(バリデーション)するようにします。

 実際、prepare関数 を使うような脆弱性の修正ではなく、脆弱性があるパラメータを int型にキャストする 修正がいくつかありました。

まとめ

 本記事で紹介したようにセキュアな WordPress プラグインを開発する場合は、脆弱性を生み出さないことを考える必要があります。
自動的に対策されるような脆弱性はほぼないと言ってもよいでしょう。その点はレガシーと感じてしまう部分となっていますが・・・。

 脆弱性が生まれやすいかつOSSなので、何でもいいから脆弱性を発見したいという方には、WordPress プラグインというのはオススメの領域だと思います。
なので日々、様々なプラグインで脆弱性が報告されている状態であり、シェアの広いプラグインであっても、深刻な脆弱性が見つかることも珍しくありません。

 PHPコードの静的解析で脆弱性を検出するツールも存在しているようです。(さわったことはありません)

 これらのことから WordPress プラグイン開発者は常にプラグインが攻撃の対象になることを意識しながら開発する必要があります。
たとえ開発したプラグインのシェアが小さくても脆弱性が報告されるということが当然のようにありますので、利用者が少ないからといって攻撃の対象にならないわけではありません。

 今回は WordPress プラグインの特色が強い脆弱性を紹介しましたが、本記事で紹介した脆弱性以外にもプラグインに潜む脆弱性は多々あります。
(例:アップロードファイルの検証が行われず、PHPファイルなどのファイルがアップロードが可能になってしまっている等)

参考

更新履歴

  • 2020年11月17日 新規作成
  • 2020年11月24日 「WordPress Plugin Security Testing Cheat Sheet」のリンク追加