flawopen.com/ファイルインクルード/PHP include()
ユーザー入力を使う PHP include() は安全ですか?
いいえ。include() は選択したファイルを PHP として解析しますが、影響は到達できるファイルと内容によって異なります。wrapper を通じたソース開示や意図しないローカルファイルの読み込みが起こり得ます。攻撃者が PHP タグ付き内容を制御し、アプリがそのファイルを読み込む場合に限り、そのコードが実行されます。リモート URL の読み込みも PHP 設定に依存します。ユーザーが選ぶキーを固定許可リストに対応付け、リクエストからパスを組み立てないでください。
<?php // User-controlled path: unintended local file include $_GET['page'] . '.php'; // Example selector: ../../private/sample // Impact depends on the resolved file and // whether it contains PHP code to interpret. // Remote URL inclusion requires the relevant // URL wrapper and allow_url_include setting. // Wrappers can change which resource is read; // php://filter is a read filter, not a path // containment check.
<?php // Allowlist: input selects a KEY, // never a path or wrapper. $pages = [ 'home' => __DIR__ . '/pages/home.php', 'about' => __DIR__ . '/pages/about.php', 'contact' => __DIR__ . '/pages/contact.php', ]; $key = $_GET['page'] ?? 'home'; if (!is_string($key) || !array_key_exists($key, $pages)) { http_response_code(404); exit; } include $pages[$key];
ローカル読み込みが自動的なコード実行を意味しない理由
ローカル読み込みだけでコードが自動実行されるわけではありません。PHP は読み込んだファイルを解析し、有効な PHP タグ内の命令を呼び出し元のコンテキストで実行します。それ以外のバイト列は出力されることがあります。実行には、攻撃者が制御する PHP を含む到達可能なファイルなどが必要です。アップロードやログ書き込みの欠陥が、制御した PHP 内容を後で読み込まれるファイルに置く場合、攻撃チェーンになり得ます。
php://filter はストリームを開く際にフィルターを適用します。脆弱な読み込み処理では、適切なフィルターで実行せずにソース文字列を露出させる場合があります。読み取り可否は wrapper、権限、設定に依存します。
パスのサニタイズだけでは不十分な理由
../ の削除、固定拡張子の追加、ディレクトリの接頭辞追加だけでは、最終リソースが許可済みとは確認できません。エンコード、絶対パス、OS の規則、wrapper により解釈が変わります。リソース名をユーザーに制御させず、アプリが定義した固定キーだけを許可してください。
パス解決が必要なら realpath() で正規化し、Path Traversal ガイドのようにディレクトリ境界内か検証します。さらに対象を許可済みファイルに限定し、シンボリックリンクや検査と使用の競合を考慮してください。固定宛先の許可リストがより安全です。
よくある質問
この設定は include/require の URL wrapper を無効にします。PHP では既定でオフで、PHP 7.4 から非推奨です。リモート読み込みのリスクは下げますが、ローカルパストラバーサルや危険なローカル include は防げません。オフのままにし、ユーザーが対象を制御できないようにします。
PHP 5.3.4 でパス処理のヌルバイト終端は修正されました。古い手法が使えないことを防御と見なさないでください。信頼できない include 先は依然危険です。
require と _once も対象を読み込み解析します。失敗時や重複時の挙動は異なりますが、信頼できない対象を安全にはしません。readfile() と file_get_contents() は PHP として解析せずデータを返すため、危険なパスは traversal や情報漏えいにつながります。