注意してください:あなたのサイトを安全でなくする可能性のあるPHPとWordPressの機能
公開: 2022-03-10WordPress(または任意の)Webサイトのセキュリティは、多面的な問題です。 サイトが安全であることを確認するために誰もが取ることができる最も重要なステップは、悪いことが起こらないようにするために単一のプロセスまたは方法では十分ではないことを覚えておくことです。 しかし、あなたが助けるためにできることがあります。 それらの1つは、否定的な結果をもたらす可能性のある関数について、作成するコードとデプロイする他のユーザーのコードを監視することです。 この記事では、WordPress開発者が使用する前に明確に考える必要のある機能について正確に説明します。
WordPress自体は、かなりの数の関数ライブラリを提供しており、その一部は危険な場合があります。 それ以外にも、WordPress(PHP)開発者が使用すると危険な頻度で使用するPHP関数がたくさんあります。
これらの関数はすべて、正当で安全な使用法がありますが、コードが悪用されて悪用されるのを容易にする関数でもあります。 セキュリティの脆弱性の原因である可能性が最も高いものと、それらに注意する必要がある理由について説明します。 まず、悪意のある人物が悪に使用できるPHP関数をコードで実行し、次に、頭痛の種となる可能性のあるWordPress固有のPHP関数について説明します。
注意すべきPHP関数
すでに述べたように、PHPには使いやすい関数がたくさん含まれています。 それらの機能のいくつかは、人々がそれらを使って悪いことを簡単に行うことができることで有名です。 これらの関数はすべて適切に使用されますが、それらの使用方法や、プロジェクトにプルする他のコードの使用方法には注意してください。
PHPのバージョンでサポートされている場合は、すでに魔法の引用符があり、グローバルの登録がオフになっていると想定します。 PHP 5以降ではデフォルトでオフになっていますが、5.4未満のバージョンではオンにすることができます。 許可したホストはほとんどありませんが、PHPのセキュリティに関する記事はそれらを含めずに完全なものではないと思います。
または、PHP 5.6がセキュリティサポートを継続している最後の5.xバージョンであることに言及します。 少なくとも、その上にいる必要があります。 また、2018年末に5.6のサポートが終了する前に、PHP7に移行する計画を立てる必要があります。
その後、対処すべきいくつかの危険な機能があります。 で始まります…
extract
は悪いニュースです、特に$_POST
または同様のもの
幸いなことに、PHPのextract
関数の使用はほとんど支持されていません。 その使用の核心は、データの配列で実行できることであり、そのキーと値のペアがコード内の生きた変数になります。 それで
$arr = array( 'red' => 5 ); extract($arr); echo $red; // 5
動作します。 これはすばらしいですが、$ $_GET
、 $_POST
POSTなどを抽出する場合も非常に危険です。これらの場合、基本的にregister_globals
の問題を自分で再現します。外部の攻撃者は、クエリ文字列を追加することで変数値を簡単に変更できます。またはフォームフィールド。 最善の解決策は単純ですextract
を使用しないでください。
自分で抽出する配列を作成する場合、 extract
を使用することは特にセキュリティ上の問題ではなく、場合によっては便利なことがあります。 しかし、それを使用するすべての場合、将来の読者を混乱させるという問題があります。 配列を作成してextract
を呼び出すことは、変数を宣言するよりも混乱を招きます。 したがって、完全に実行不可能でない限り、代わりに手動で宣言することをお勧めします。
ユーザー入力でextract
を使用する必要がある場合は、常にEXTR_SKIP
フラグを使用する必要があります。 これにはまだ混乱の問題がありますが、単純なクエリ文字列またはWebフォームの変更を介して、悪意のある部外者がプリセット値を変更する可能性を排除します。 (すでに設定されている値を「スキップ」するためです。)
任意のコードが怖いので「 eval
」
PHPおよびそれを実行する他のほぼすべての言語でのeval
は、常にこのようなリストの上位にあります。 そして、正当な理由があります。 PHP.netに関するPHPの公式ドキュメントには、率直に言って次のように書かれています。
注意:eval()言語構造は、任意のPHPコードを実行できるため、非常に危険です。 したがって、その使用はお勧めしません。 この構成を使用する以外に選択肢がないことを注意深く確認した場合は、事前に適切に検証せずに、ユーザーが提供したデータを渡さないように特に注意してください。
eval
を使用すると、プログラム内の任意の文字列をPHPコードであるかのように実行できます。 つまり、それ自体がプログラムを構築できるプログラムを構築する「メタプログラミング」に役立つということです。 また、任意のソース(Webページのテキストボックスなど)をeval
文字列エバリュエーターにすぐに渡すことを許可すると、悪意のある攻撃者がPHPでできることのほとんどすべてを簡単に実行できるようになるため、非常に危険です。サーバーで行います。 これには、明らかに、データベースへの接続、ファイルの削除、およびマシンにSSHで接続されたときに誰かが実行できるその他のことすべてが含まれます。 これは悪いです。
プログラムでeval
を使用する必要がある場合は、邪魔にならないようにして、任意のユーザー入力がプログラムに渡されないようにする必要があります。 また、任意のユーザーにeval
の入力へのアクセスを許可する必要がある場合は、実行させないコマンドのブラックリスト、または(より良いが、実装がはるかに難しい)コマンドのみのホワイトリストを介してユーザーが実行できることを制限します。安全だと考えてください。 さらに良いことに、検証された整数のみのように、特定のパラメーターの変更を少数だけ許可します。
ただし、PHPの創設者であるRasmusLerdorfからの次の行を常に覚えておいてください。
「 `eval()`が答えである場合、あなたはほぼ間違いなく間違った質問をしているでしょう。」
eval
のバリエーション
よく知られているeval
に加えて、PHPが歴史的に文字列(評価済み)をコードとしてサポートしてきたさまざまな方法があります。 WordPress開発者に最も関連する2つは、 /e
修飾子を指定したpreg_replace
とcreate_function
です。 それぞれの動作は少し異なり、PHP5.5.0以降のpreg_replace
はまったく機能しません。 (PHP 5.5以下は、公式のセキュリティ更新プログラムを取得していないため、使用しないことをお勧めします。)
/e
正規表現の修飾子も「悪」です
PHP 5.4.x以下を実行している場合は、eで終わるpreg_replace
呼び出しに注意する必要があります。 これは次のようになります。
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
$html = preg_replace( '( (.*?) )e', '" " . strtoupper("$2") . " "', $html );) // or $html = preg_replace( '~ (.*?) ~e', '" " . strtoupper("$2") . " "', $html );)
PHPには、正規表現で「フェンス」するさまざまな方法がありますが、注意したいのは、 preg_replace
の最初の引数の最後の文字である「e」です。 そこにある場合は、実際に見つかったセグメント全体を引数としてインラインPHPに渡し、それをeval
します。 これには、ユーザー入力を関数に入れさせる場合のeval
とまったく同じ問題があります。 サンプルコードは、代わりにpreg_replace_callback
を使用して置き換えることができます。 これの利点は、関数を書き留めているため、攻撃者が評価対象を変更するのが難しいことです。 したがって、上記を次のように記述します。
// uppercase headings $html = preg_replace_callback( '( (.*?) )', function ($m) { return " " . strtoupper($m[2]) . " "; }, $html );
// uppercase headings $html = preg_replace_callback( '( (.*?) )', function ($m) { return " " . strtoupper($m[2]) . " "; }, $html );
// uppercase headings $html = preg_replace_callback( '( (.*?) )', function ($m) { return " " . strtoupper($m[2]) . " "; }, $html );
// uppercase headings $html = preg_replace_callback( '( (.*?) )', function ($m) { return " " . strtoupper($m[2]) . " "; }, $html );
// uppercase headings $html = preg_replace_callback( '( (.*?) )', function ($m) { return " " . strtoupper($m[2]) . " "; }, $html );
ユーザーcreate_function
を入力させるのも悪い…
PHPにはcreate_function
関数もあります。 これは現在PHP7.2で非推奨になっていますが、 eval
と非常によく似ており、同じ基本的な欠点がありました。文字列(2番目の引数)を実行可能なPHPに変換できるということです。 また、同じリスクがありました。注意しないと、誤ってスマートクラッカーにサーバー上で何かを実行する機能を与えるのは簡単すぎます。
これは、5.3を超えるPHPを使用している場合、 preg_replace
よりも修正が簡単です。 文字列を仲介として使用せずに、独自の無名関数を作成できます。 これは、少なくとも私の目には、より安全で読みやすいものです。
assert
Is also eval
-Like
assert
は、WordPress内外で、多くのPHP開発者が使用している関数ではありません。 その目的は、コードの前提条件に関する非常に軽量なアサーションです。 ただし、 eval
タイプの操作もサポートしています。 そのため、 eval
と同じように注意する必要があります。 文字列ベースのアサーション(これが悪い理由の核心)もPHP 7.2で非推奨になりました。つまり、将来的にはそれほど心配する必要はありません。
可変ファイルを含めることは、制御されていないPHPの実行を可能にする方法です
eval
が悪い理由についてはかなりよく調べましたが、 include
やrequire($filename'.php')
ようなものは、特に$filename
がユーザー制御可能な値から設定されている場合、同様に悪いニュースになる可能性があります。 その理由はeval
とは微妙に異なります。 可変ファイル名は、WordPress以外のPHPアプリでの単純なURLからファイルへのルーティングなどによく使用されます。 しかし、WordPressでも使用されているのを目にするかもしれません。
include
またはrequire
(またはinclude_once
またはrequire_once
)すると、スクリプトにインクルードされたファイルを実行させるという問題の核心です。 それは、多かれ少なかれ、そのファイルを概念的にeval
していますが、そのように考えることはめったにありません。
変数include
可能性のあるすべてのファイルを作成し、それが実行されたときに何が起こるかを検討した場合は、問題ありません。 しかし、 password.php
またはwp-config.php
をinclude
ことで何ができるかを考慮していなければ、それは悪いニュースです。 また、誰かが悪意のあるファイルを追加してからinclude
を実行する可能性がある場合も悪いニュースです(ただし、その時点ではおそらくより大きな問題が発生します)。
ただし、これに対する解決策はそれほど難しくありません。ハードコードには、可能な場合は含まれます。 できない場合は、含めることができるファイルのホワイトリスト(より良い)またはブラックリストのいずれかを用意してください。 ホワイトリスト内のファイル(つまり、追加されたときに何が行われるかを監査した)であれば、安全であることがわかります。 ホワイトリストに含まれていない場合、スクリプトには含まれません。 その簡単な調整で、あなたはかなり安全です。 ホワイトリストは次のようになります。
$white_list = ['db.php', filter.php', 'condense.php'] If (in_array($white_list, $file_to_include)) { include($file_to_include); }
ユーザー入力をshell_exec
およびバリアントに渡さないでください
これは大きなものです。 PHPのshell_exec
、 system
、 exec
、およびbackticksはすべて、実行しているコードが基盤となる(通常はUnix)シェルと通信できるようにします。 これは、 eval
を危険なものにするものと似ていますが、2倍になります。 ここでユーザー入力を不注意に許可すると、攻撃者はPHPの制約に縛られることさえないため、2倍になります。
PHPからシェルコマンドを実行する機能は、開発者として非常に役立ちます。 しかし、ユーザーの入力をそこに入れると、ユーザーは密かに多くの危険な力を獲得することができます。 したがって、ユーザー入力をshell_exec
タイプの関数に渡してはならないということまで言います。
このタイプの状況を処理するために私が考えることができる最善の方法は、それを実装したい場合、ユーザーに既知の安全な事前定義されたシェルコマンドの小さなセットへのアクセスを提供することです。 それを確保することは可能かもしれません。 しかし、それでも私はあなたに非常に注意するように警告します。
unserialize
; コードを自動的に実行します
生きているPHPオブジェクトでserialize
を呼び出し、そのデータをどこかに保存し、後でその保存された値を使用してそのオブジェクトのunserialize
を解除するというコアアクションはすばらしいものです。 これもかなり一般的ですが、リスクを伴う可能性があります。 なぜ危険なのですか? そのunserialize
呼び出しへの入力が完全に安全でない場合(たとえば、データベースではなくCookieとして保存されている場合)、攻撃者はunserialize
呼び出しに何か悪いことをさせる方法でオブジェクトの内部状態を変更する可能性があります。
このエクスプロイトは、 eval
の問題よりも秘教的であり、気付かれにくいです。 ただし、シリアル化されたデータのストレージメカニズムとしてCookieを使用している場合は、そのデータにserialize
を使用しないでください。 json_encode
やjson_decode
のようなものを使用してください。 これらの2つを使用すると、PHPはコードを自動実行しません。
ここでの主な脆弱性は、PHPが文字列をクラスにunserialize
解除するときに、そのクラスで__wakeup
メソッドを呼び出すことです。 検証されていないユーザー入力をunserialized
しないことが許可されている場合、データベース呼び出しや__wakeup
メソッドでのファイル削除などが、危険な場所または望ましくない場所に向けられる可能性があります。
unserialize
は、オブジェクトに対して魔法のメソッドを使用する必要があるため、 eval
の脆弱性とは異なります。 攻撃者は、独自のコードを作成するのではなく、オブジェクトに対して既に作成されたメソッドを悪用することを余儀なくされます。 このOWASPWikiページで説明されているように、オブジェクトの__destruct
と__toString
の両方のマジックメソッドも危険です。
一般に、クラスで__wakeup
、 __destruct
、または__toString
メソッドを使用しなくても問題ありません。 ただし、後で誰かがそれらをクラスに追加するのを目にする可能性があるため、呼び出しの近くにいるユーザーに、JSON( serialize
やjson_decode
)のようなものを介して、その種の使用のためにすべての公開データをunserialize
および逆シリアル化して渡さないようにすることをおjson_encode
します。自動コード実行ではありません。
file_get_contents
を使用してURLを取得するのは危険です
外部URLを呼び出さなければならないPHPコードをすばやく作成する場合の一般的な方法は、 file_get_contents
にアクセスすることです。 迅速で簡単ですが、安全性はそれほど高くありません。
file_get_contents
の問題は微妙ですが、ホストがPHPを構成して、外部URLにアクセスすることさえできないようにすることはよくあることです。 これはあなたを保護するためのものです。
ここでの問題は、 file_get_contents
がリモートページをフェッチすることです。 ただし、その場合、HTTPSプロトコル接続の整合性はチェックされません。 つまり、スクリプトが中間者攻撃の犠牲になる可能性があり、攻撃者が必要なものをfile_get_contents
ページの結果に入れる可能性があります。
これはより難解な攻撃です。 しかし、最新の(Composerベースの)PHPを作成するときにそれを防ぐために、ほとんどの場合、Guzzleを使用してより安全なcURLAPIをラップしています。 WordPressでは、さらに簡単ですwp_remote_get
を使用します。 file_get_contents
よりもはるかに一貫して機能し、デフォルトでSSL接続の検証になります。 (これをオフに切り替えることはできますが、そうではないかもしれません…)さらに良いですが、話すのが少し面倒ですが、 wp_safe_remote_get
などです。これらは名前にsafe_
がない関数と同じように機能しますが、安全でないリダイレクトや転送が途中で発生しないようにしてください。
filter_var
からのURL検証を盲目的に信頼しないでください
ですから、これは少しわかりにくいので、このWordCampの講演で説明してくれたChrisWeigmanに小道具を送ります。 一般に、PHPのfilter_var
は、データを検証またはサニタイズするための優れた方法です。 (ただし、何をしようとしているのか混乱しないでください…)
ここでの問題はかなり具体的です。URLが安全であることを確認するためにそれを使用しようとしている場合、 filter_var
はプロトコルを検証しません。 これは多くの場合問題にはなりませんが、検証のためにこのメソッドにユーザー入力を渡し、 FILTER_VALIDATE_URL
を使用している場合は、 javascript://comment%0aalert(1)
のようなものが通過します。 つまり、これは、予期しない場所での基本的なXSS攻撃の非常に優れたベクトルになる可能性があります。
URLを検証するために、WordPressのesc_url
関数も同様の影響を及ぼしますが、許可されたプロトコルのみを通過させます。 javascript
はデフォルトのリストにないので、安全に保つことができます。 ただし、 filter_var
とは異なり、渡された許可されていないプロトコルに対しては空の文字列(falseではない)が返されます。
目を離さないWordPress固有の機能
コアPHPの潜在的に脆弱な関数に加えて、ちょっとした落とし穴になる可能性のあるWordPress固有の関数がいくつかあります。 これらのいくつかは、上記のさまざまな危険な機能に非常に似ていますが、少し異なります。
WordPressはmaybe_unserialize
でシリアル化を解除します
上記を読めば、これはおそらく明らかです。 WordPressには、 maybe_unserialize
という関数があり、ご想像のとおり、必要に応じて渡されたものを逆シリアル化します。
これによって発生する新しい脆弱性はありません。問題は、コアのunserialize
関数と同様に、非シリアル化時に脆弱なオブジェクトが悪用される可能性があることです。
is_admin
は、ユーザーが管理者の場合は応答しません。
これはかなりシンプルですが、機能の名前が曖昧なので、人を混乱させたり、急いでいると見落とされがちです。 WordPressでアクションを実行しようとしているユーザーが、そのアクションを実行するために必要な権限と特権を持っていることを常に確認する必要があります。 そのためには、 current_user_can
関数を使用する必要があります。
ただし、誤って、 is_admin
は、現在のユーザーが管理者レベルのアカウントであるかどうかを通知し、プラグインが使用するオプションを設定できるはずだと考えるかもしれません。 これは間違いです。 代わりに、WordPressでis_admin
が行うことは、現在のページの読み込みがサイトの管理側(前面側)にあるかどうかを通知することです。 したがって、管理ページ(「ダッシュボード」など)にアクセスできるすべてのユーザーは、このチェックに合格できる可能性があります。 is_admin
が現在のユーザーではなく、ページの種類に関するものであることを覚えている限り、問題はありません。
add_query_arg()
はURLをサニタイズしません
これはそれほど一般的ではありませんが、この関数に関する公開ドキュメントが正しくなかったため、数年前にWordPressエコシステムに大きな更新の波がありました。 主要な問題は、URLが渡されなかった場合にadd_query_arg
関数(およびその逆remove_query_arg
)がサイトのURLを自動的にサニタイズしないことであり、人々はそれをサニタイズすると考えていました。 多くのプラグインはCodexによって誤解されており、結果としてこれを安全に使用していませんでした。
彼らがしなければならなかった中心的なことは、この関数を使用する前に、この関数の呼び出しの結果をサニタイズすることです。 そうすれば、あなたは彼らがそうだと思っていたXSS攻撃から本当に安全です。 つまり、次のようになります。
echo esc_url( add_query_arg( 'foo', 'bar' ) );
$wpdb->query()
はSQLインジェクション攻撃に対してオープンです
SQLインジェクションについて知っているなら、これはばかげているように思えるかもしれません。 これは、SQLインジェクション攻撃を可能にするデータベースクエリを作成するためにデータベースにアクセスする方法(たとえば、PHPのmysqli
またはPDOデータベースドライバーを使用する方法)であるためです。
私が特に$wpdb
$wpdb->query
を呼び出している理由は、$ wpdbの他のいくつかのメソッド( insert
、 delete
など)がインジェクション攻撃を処理するためです。 さらに、 WP_Query
などを使用して基本的なWordPressデータベースクエリを作成することに慣れている場合は、SQLインジェクションを考慮する必要はありません。 これが私がそれを呼んでいる理由です:最初に$wpdb
を使用して独自のクエリを作成しようとするときに、データベースへのインジェクション攻撃が可能であることを確実に理解するためです。
何をすべきか? $wpdb->prepare()
を使用してから、 $wpdb->query()
を使用します。 また、 get_row()
やget_var()
)などの$wpdb
の他の「getting」メソッドの前に準備することを確認する必要があります。 そうでなければ、ボビーテーブルはあなたを得るかもしれません。
esc_sql
もSQLインジェクションからあなたを保護しません
ほとんどのWordPress開発者にとって、 esc_sql
は意味を持たずに登録されていると思いますが、登録する必要があります。 先ほど述べたように、データベースクエリを実行する前に、 wpdb->prepare()
を使用する必要があります。 これはあなたを安全に保ちます。 しかし、開発者が代わりにesc_sql
に手を伸ばすのは魅力的で理解しやすいことです。 そして、彼らはそれが安全だと期待するかもしれません。
問題は、 esc_sql
にはSQLインジェクションに対する強力な保護がないことです。 これは、実際にはPHPのadd_slashes
関数の栄光のバージョンであり、データベースを保護するために何年も使用することをお勧めしていません。
やるべきことはまだたくさんありますが、これは大きなスタートです
攻撃者が悪用する可能性のあるコード内の関数を監視するだけでなく、セキュリティにはさらに多くのことがあります。 たとえば、ユーザーから受け取ったすべてのデータを検証およびサニタイズし、Webページに配置する前にエスケープする必要性については、あまり詳しく説明しませんでした(ただし、最近、そのトピックに関する記事「WordPressの保護」を公開しました。クロスサイトスクリプティング攻撃に対するサイト」)。 ただし、これをより広範なセキュリティ戦略の一部として使用することはできますし、使用する必要があります。
WordPressに新しいプラグインをデプロイする前に最終検査を行うときに、この誤用しやすい関数のリストを脇に置いておくことは素晴らしいアイデアです。 ただし、ホスティングのセキュリティを信頼していること、ユーザーが適切なパスワードを持っていることなどを確認する必要もあります。
セキュリティについて覚えておくべき重要なことは、最も弱いリンクが重要なリンクであるということです。 ある分野での優れた実践は、他の場所で起こりうる弱点を補強しますが、それを完全に修正することはできません。 ただし、これらの機能に注意してください。そうすれば、ほとんどの機能を強化できます。