注: この投稿を続けるには、PHP の最小限のプログラミング知識があることが前提となります。
この投稿は、お気に入りの CMS やフレームワークの先頭で見たことがあるかもしれない PHP コードの断片に関するものであり、セキュリティのために、すべての PHP ファイルのヘッダーに常に含める必要があると読んだことがあるでしょう。その理由については明確な説明がありませんが、開発します。私はこのコードを参照しています:
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
このタイプのコードは WordPress ファイルでは非常に一般的ですが、実際にはほとんどすべてのフレームワークと CMS に存在します。たとえば、CMS Joomla の場合、唯一の変更点は、ABSPATH の代わりに JEXEC が使用されることです。それ以外の場合、ロジックは同じです。この CMS は、Mambo と呼ばれる別の CMS から派生したもので、これも同様のコードを使用していましたが、定数として _VALID_MOS を使用していました。さらに時間を遡ると、このタイプのコードを使用した最初の CMS は PHP-Nuke であることがわかります (PHP の最初の CMS であると考える人もいます)。
PHP-Nuke (および今日のほとんどの CMS およびフレームワーク) の実行フローは、Web 上でユーザーまたは訪問者が実行したアクションに一緒に応答する複数のファイルを順次ロードすることで構成されていました。つまり、example.net ドメインの下にこの CMS がインストールされた当時の Web サイトを想像してください。ホームページがロードされるたびに、システムは一連のファイルを順序立てて実行します (この場合、これは単なる例であり、実際のシーケンスではありません)。 load_modules.php =>モジュール.php。つまり、このシーケンスでは、index.php が最初にロードされ、次にこのスクリプトがload_modules.php をロードし、次に modules.php. がロードされます。
この実行チェーンは、必ずしも最初のファイル (index.php) から始まるわけではありません。実際、URL (例: http://example.net/load_modules.php または http://example.net/modules.php) で他の PHP ファイルの 1 つを直接呼び出すことで、誰でもこのフローの一部をスキップできます。 、これから見ていきますが、多くの場合危険である可能性があります。
この問題はどのように解決されましたか? セキュリティ対策が導入され、次のようなコードが各ファイルの先頭に追加されました。
<?php if (!eregi("modules.php", $HTTP_SERVER_VARS['PHP_SELF'])) { die ("You can't access this file directly..."); }
基本的に、このコードは modules.php というファイルのヘッダーにあり、URL 経由で modules.php に直接アクセスされているかどうかを確認しました。存在する場合、実行は停止され、「このファイルには直接アクセスできません...」というメッセージが表示されます。 $HTTP_SERVER_VARS['PHP_SELF'] に modules.php が含まれていない場合は、通常の実行フローにあり、続行が許可されていることを意味します。
ただし、このコードにはいくつかの制限がありました。まず、コードは挿入されるファイルごとに異なるため、複雑さが増しました。さらに、特定の状況では、PHP が $HTTP_SERVER_VARS['PHP_SELF'] に値を割り当てなかったため、有効性が制限されました。
それで、開発者は何をしたのでしょうか?彼らは、これらのコードの断片をすべて、よりシンプルで効率的なバージョンに置き換えました。
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
この新しいコードは、PHP コミュニティではすでにかなり一般的でしたが、定数の存在が確認されました。この定数は、フローの最初のファイル (index.php または home.php または類似のファイル) で定義され、値が割り当てられました。したがって、この定数がストリーム内の他のファイルに存在しない場合は、誰かがindex.php ファイルをスキップして別のファイルに直接アクセスしようとしたことを意味します。
きっとこの時点で、あなたは死刑の連鎖を断ち切ることが世界で最も重大なことであるに違いないと考えているでしょう。しかし、実際には、通常、それは重大な危険を意味するものではありません
。PHP エラーによってファイルへのパスが公開されると、危険が生じる可能性があります。サーバー上でエラー抑制が設定されていれば、これは心配する必要はありません。また、たとえエラーが隠蔽されていなかったとしても、公開される情報は最小限であり、攻撃者に与えられる可能性のある手がかりはほんのわずかです。
誰かが HTML の断片を含むファイルに (ビューから) アクセスし、コンテンツの一部が公開される可能性もあります。ほとんどの場合、これも心配する必要はありません。
最後に、開発者が不注意または経験不足により、実行フローの途中で外部依存関係のない危険なコードを挿入してしまう可能性があります。通常、フレームワークまたは CMS のコードは、その実行のために他のクラス、関数、または外部変数に依存するため、これは非常に珍しいことです。したがって、URL を介してスクリプトを直接実行しようとすると、これらの依存関係を見つけることができず、実行を続行できません。
では、ほとんど心配する必要がないのに、なぜ定数コードを追加するのでしょうか?その理由は次のとおりです。「この方法は、レジスタ グローバル への攻撃による偶発的な変数挿入も防止し、実際にはアプリケーション内にないのに PHP ファイルがアプリケーション内にあると想定するのを防ぎます。」
PHP の開始以来、URL (GET) またはフォーム (POST) を通じて送信されるすべての変数は自動的にグローバルに変換されました。つまり、download.php?filepath=/etc/passwd ファイルにアクセスした場合、download.php ファイル (および実行フローでそれに依存するファイル) で echo $filepath を使用できます。結果は /etc/passwd になります。
download.php 内では、$filepath 変数が実行フロー内の以前のファイルによって作成されたのか、それとも誰かが URL や POST 経由で偽装したのかを知る方法がありませんでした。これにより、大きなセキュリティ ホールが発生しました。 download.php ファイルに次のコードが含まれていると仮定して、例で見てみましょう:
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
開発者はおそらく、フロント コントローラー パターンを使用してコードを実装すること、つまり、すべての Web リクエストが 1 つの入力ファイル (index.php、home.php など) を通過するようにすることを考えたのでしょう。このファイルは、セッションの初期化、共通変数のロード、そして最後にファイルをダウンロードするためにリクエストを特定のスクリプト (この場合は download.php) にリダイレクトする役割を果たします。
ただし、攻撃者は、前述のように、download.php?filepath=/etc/passwd を呼び出すだけで、計画された実行シーケンスをバイパスする可能性があります。したがって、PHP は値 /etc/passwd を持つグローバル変数 $filepath を自動的に作成し、攻撃者がそのファイルをシステムからダウンロードできるようにします。重大な間違いです。
これは氷山の一角にすぎず、最小限の努力でさらに危険な攻撃が実行される可能性があります。たとえば、次のようなコードでは、プログラマが未完成のスクリプトとして残す可能性があります:
<?php if (!eregi("modules.php", $HTTP_SERVER_VARS['PHP_SELF'])) { die ("You can't access this file directly..."); }
攻撃者は、リモート ファイル インクルージョン (RFI) 攻撃を使用してあらゆるコードを実行する可能性があります。したがって、攻撃者が実行したいコードを含む My.class.php ファイルを自分のサイト https://mysite.net に作成した場合、そのファイルに自分のドメインを渡すことで脆弱なスクリプトを呼び出すことができます: codigo_inutil.php?base_path= https:// mysite.net となり、攻撃は完了しました。
別の例: 次のコードを含む、remove_file.inc.php というスクリプト内:
<?php if (!defined('MODULE_FILE')) { die ("You can't access this file directly..."); }
攻撃者は、remove_file.inc.php?filename=/etc/hosts のような URL を使用してこのファイルを直接呼び出し、システムから /etc/hosts ファイルを削除しようとする可能性があります (システムが許可している場合、またはその他の場合)。削除権限のあるファイル)。
内部的にグローバル変数も使用する WordPress のような CMS では、このタイプの攻撃は壊滅的でした。しかし、継続的な技術のおかげで、これらおよび他の PHP スクリプトは保護されました。最後の例で見てみましょう:
<?php if ( ! defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly }
誰かがremove_file.inc.php?filename=/etc/hostsにアクセスしようとすると、定数によってアクセスがブロックされます。これが変数である場合、当然、攻撃者がそれを挿入する可能性があるため、定数であることが重要です。
この時点で、これほど危険な機能であるのに、なぜ PHP がこの機能を保持していたのか疑問に思われるでしょう。また、他のスクリプト言語 (JSP、Ruby など) を知っている場合は、それらに類似した言語がないことがわかります (そのため、定数テクニックも使用されません)。 PHP は C のテンプレート システムとして誕生し、この動作により開発が容易になったことを思い出してください。良いニュースとして、PHP のメンテナは、それが引き起こす問題を見て、この機能を無効にできるように register_globals (デフォルトでアクティブ化) と呼ばれるディレクティブを php.ini に導入することを決定したことです。
しかし、問題が解決しないため、デフォルトで無効にしました。それでも、当時のコードの多くは GET/POST/... 値にアクセスするために推奨されている HTTP_*_VARS 変数を使用していなかったので、多くのホストはクライアントのプロジェクトが機能しなくなることを恐れて、これを有効にし続けました。むしろグローバル変数です。
最終的に、状況が変わらないことを見て、彼らは思い切った決断を下しました。これらすべての問題を回避するために、PHP 5.4 ではこの機能を削除しました。したがって、今日では、これまで見てきたようなスクリプト (定数を使用しない) は、特定の場合に無害な警告/通知を除いて、通常 危険をもたらすことはなくなりました。
今日でも、一定のテクニックが一般的です。しかし、悲しいことに、そしてこの投稿に至った理由は、その使用の本当の理由を知っている開発者がほとんどいないことです。
過去の他の優れた実践法 (呼び出し内の参照による危険を避けるために関数内のパラメーターをローカル変数にコピーする、プライベート変数にアンダースコアを使用して区別するなど) と同様に、誰かが一度やったという理由だけで今でもこの手法を適用している人はたくさんいます。は、それが現在の時代に本当に価値をもたらすかどうかを考慮することなく、それが良い習慣であると彼らに言いました。現実には、大多数の場合、このテクニックはもはや必要ありません。
この慣行が妥当性を失った理由は次のとおりです。
*register globals の消滅: PHP 5.4 以降、GET 変数と POST 変数を PHP グローバル変数として登録する機能は存在しません。これまで見てきたように、*register globals を使用しないと、個々のスクリプトの実行は無害になり、この慣行の主な理由が排除されます。
現在のコードの設計の改善: PHP 5.4 より前のバージョンであっても、最新のコードはより適切に設計され、クラスと関数で構造化されているため、外部変数を介したアクセスや操作が複雑になります。グローバル変数を頻繁に使用する WordPress でさえ、これらのリスクを最小限に抑えます。
*front-controllers の使用: 現在、ほとんどの Web アプリケーションは、適切に設計された *front-controllers を使用しています。これにより、クラスと関数のコードが実行チェーンがメイン エントリ ポイントで開始される場合にのみ実行されます。したがって、誰かがファイルを単独でアップロードしようとしても問題ありません。フローが適切な時点から開始されない場合、ロジックはアクティブになりません。
クラスの自動ロード: 現在の開発ではクラスの自動ロードが使用されているため、include または require の使用は大幅に減りました。これは、初心者の開発者でない限り、リスクをもたらす可能性のある includes や requires があってはならないことを意味します (Remote File Inclusion や など)ローカル ファイル インクルード).
パブリック コードとプライベート コードの分離: 多くの最新の CMS およびフレームワークでは、パブリック コード (アセット など) がプライベート コード (プログラミング コード) から分離されています。この措置は、サーバー上で PHP に障害が発生した場合に、PHP ファイル内のコード (定数テクニックを使用しているかどうかに関係なく) が公開されないようにするため、特に価値があります。これはグローバルの登録を軽減するために特別に実装されたものではありませんが、他のセキュリティ問題を回避するのに役立ちます。
フレンドリー URL の拡張使用: 現在では、フレンドリー URL を使用するようにサーバーを構成するのが一般的であり、これにより、プログラミングには常に単一のエントリ ポイントが強制されます。これにより、誰も PHP ファイルを単独でアップロードすることがほぼ不可能になります。
運用環境でのエラー出力の抑制: ほとんどの最新の CMS とフレームワークはデフォルトでエラー出力をブロックするため、攻撃者はアプリケーションの内部動作に関する手がかりを見つけることができません。これは、他の種類のエラーを促進する可能性があります。
この手法は大多数の場合には必要なくなりましたが、決して役に立たないという意味ではありません。プロの開発者として、各ケースを分析し、一定のテクニックが作業している特定の状況に関連するかどうかを判断することが不可欠です。これは、たとえ良い習慣であると考える場合であっても、常に適用する必要がある基準です。
一定のテクニックをいつ適用すべきかまだわからない場合は、次の推奨事項が参考になります。
学び続けてください...
以上がフレームワークと CMS の奇妙な PHP コードの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。