PHP の disable_functions は、exec() や system() のような危険な関数を使えなくする設定です。便利で、多くの現場で入っています。ただし、これをアプリを守る「壁」だと思って頼るのは危険です。disable_functions は関数を名前で呼ぶことだけを止める保険で、セキュリティ境界(サンドボックス)ではありません。最近も、PHP のメモリバグ1つで disable_functions を回避したとする実証コードが公開されました(真偽は未検証ですが、頼れない理由をよく表しています)。この記事では、なぜ頼れないのか、そして本当の隔離をどこに持つのかを整理します。攻撃の手順は扱いません。
WHAT IT STOPS vs NOTdisable_functions は「名前で関数を呼ぶ」ことだけを止める
名前で呼ぶ exec() は止まる。名前を使わない呼び出しは止まらない
止められるもの
設定した名前の関数を、コードから呼んだとき
- exec()・system()・shell_exec() などを名前で呼ぶ
- うっかり有効な関数を本番に残した場合の保険
- 攻撃対象の面を減らす(多層防御の1枚)
止められないもの
名前での呼び出し検査をすり抜ける経路
- メモリ破壊バグで、関数を名前を使わず直接呼ぶ
- 新しい実行形態(SAPI)やコーダー経由の間接実行
- ファイルの読み書き・ネットワークなど止めていない関数
- 設定ミスで ini_set や .user.ini から戻される
30秒でわかる
- ・何か:
disable_functionsは、指定した関数を名前で呼ぶのを止める php.ini の設定 - ・限界:名前の検査にすぎない。PHP のメモリバグや新しい実行形態で、名前を使わず関数が呼ばれると効かない
- ・歴史:PHP は似た狙いの safe_mode を「PHP 層で解くのは設計的に誤り」として 5.4 で廃止している
- ・結論:保険として併用しつつ、本当の壁は OS・コンテナ側(低権限ユーザー・隔離・seccomp)に持つ
依存部品やビルドまで含めた守り方は サプライチェーン攻撃とは|手口・防御ツール比較・npm 12の新機構まで実践解説 に、PHP 本体の脆弱性が出たときの当て方は PHP 緊急アップデートを数分で本番に当てる|事前に仕込むDockerイメージのビルド高速化と配布基盤の作り方 にまとめています。
disable_functions は何をしているか
disable_functions は php.ini で設定する、「この関数は使わせない」という名前のリストです。
; php.ini(PHP_INI_SYSTEM:アプリのコードからは変えられない)
disable_functions = "exec,passthru,shell_exec,system,proc_open,popen,pcntl_exec"
PHP は、コードが関数を名前で呼ぶたびに、その名前がこのリストにあるかを見て、あれば止めます。ポイントは、これが「呼び出しのときの、名前による検査」だということです。だから、うっかり system() を残したアプリや、テンプレートに紛れ込んだ呼び出しは止められます。攻撃者が使える道具を1つ減らす、多層防御の1枚としては有効です。
disable_functions は php.ini でしか設定できず、バーチャルホストやディレクトリごとに分けることはできません(PHP マニュアルの記載)。PHP_INI_SYSTEM なので、アプリのコードから ini_set() で戻すこともできません。ここまでは、設定として素直です。
なぜ「壁」にならないのか
問題は、名前で呼ぶのを止めているだけだという点です。名前での検査をすり抜ける経路が、いくつもあります。
| すり抜ける経路 | 何が起きるか |
|---|---|
| PHP 本体のメモリ破壊バグ | 攻撃者がメモリを壊して、関数を名前を使わずに直接呼ぶ。リストは参照されない |
| 新しい実行形態・拡張の経路 | 想定していなかった SAPI や拡張・コーダー経由で、間接的にコードが動く |
| 止めていない関数 | ファイルの読み書き・ネットワーク・ストリームなど、リストに無い関数で目的を達する |
| 設定の抜け | .user.ini の扱いや別の php.ini が優先され、意図した制限が効いていない |
いちばん効かないのが1行目です。unserialize() や画像処理のような経路に PHP 本体のメモリ破壊バグがあると、攻撃者はエンジンのメモリを組み替えて、disable_functions の検査を通らずに関数を呼べます。実際、2026年11月には「PHP のメモリ解放後使用(use-after-free)を突いて disable_functions を回避した」とする実証コードが公開されました(第三者による主張で、当サイトでは真偽を検証していません)。真偽はともかく、エンジンのバグ1つで無効化されうるという性質そのものが、disable_functions を「壁」にできない理由です。
これは disable_functions の欠陥というより、PHP の設定で隔離を作ろうとすること自体の限界です。PHP は、1つのプロセスの中でアプリのコードを動かす仕組みで、OS のような権限の境界を内側に持っていません。
PHP 自身が safe_mode を捨てた理由
同じ考え方は、PHP の歴史にも出てきます。かつて PHP には、共有サーバーでユーザーどうしを分離しようとする safe_mode がありました。PHP はこれを 5.3 で非推奨にし、5.4 で削除しています。当時のマニュアルは、この手の問題を PHP の層で解こうとするのは設計的に誤りだとしていました(レンタルサーバー事業者が、OS やウェブサーバー側の手段が現実的でなかった当時、やむなく使っていた、とも書かれています)。
disable_functions は safe_mode とは別の設定ですが、「PHP の中で危ないものを名前で止める」という発想は同じで、同じ限界を引き継いでいます。PHP を作っている側が「PHP の層で隔離は解けない」と結論づけていることは、設定に頼りすぎないための強い根拠になります。
本当の隔離は OS 側に持つ
では何をするか。disable_functions は保険として残しつつ、壁は PHP の外に作ります。考え方は「PHP のプロセスが乗っ取られても、被害をそのプロセスの箱の中に閉じ込める」ことです。
REAL ISOLATION本当の隔離は OS 側に持つ(PHP の設定ではない)
PHP をroot で動かさない。壁は OS とコンテナで作る
-
1.専用の低権限ユーザーで動かす
PHP-FPM のプールを、書き込める範囲が狭い専用ユーザーに。root や共有ユーザーで動かさない
-
2.コンテナ・名前空間で区切る
1つのアプリが落ちても他に波及しないよう、プロセス・ファイル・ネットワークを分ける
-
3.seccomp で呼べるシステムコールを絞る
コンテナのランタイムで、不要なシステムコール(新しいプロセスの生成など)を禁止
-
4.書き込みと公開範囲を最小に
アプリの置き場は読み取り専用に。アップロード先は実行不可でマウント。外に出る通信も絞る
- 専用の低権限ユーザーで動かす。 PHP-FPM のプールを、書き込める場所が限られた専用ユーザー(
www-dataを共有せず、アプリごとに分ける)で動かします。root や、他のサービスと共有のユーザーで動かさない。乗っ取られたとき、そのユーザーが触れる範囲が被害の上限になります。 - コンテナ・名前空間で区切る。 アプリをコンテナに入れ、プロセス・ファイルシステム・ネットワークを分けます。1つが落ちても、他のアプリやホストに波及しないようにします。
- seccomp で呼べるシステムコールを絞る。 コンテナのランタイム(Docker/containerd など)で seccomp プロファイルを使い、新しいプロセスの生成など、アプリが使わないシステムコールを禁止します。コマンド実行に至る最後の一歩を、OS の側で塞ぎます。
- 書き込みと公開範囲を最小にする。 アプリの配置先は読み取り専用でマウントし、アップロードの保存先は実行不可(
noexec)にします。外に出る通信(アウトバウンド)も、必要な宛先だけに絞ります。これで、仮にコードを書き込まれても実行させない・持ち出させない形にします。
open_basedir のような PHP 側の設定も、ファイルアクセスの範囲を狭める保険にはなります。ただしこれも PHP の中の制限なので、同じく「壁」ではなく保険として、OS 側の隔離と重ねて使います。
まとめ:保険と壁を分けて考える
要点
- ・
disable_functionsは「名前で関数を呼ぶ」のを止める保険。メモリバグ1つで回避されうる - ・PHP は似た狙いの safe_mode を「PHP 層で解くのは誤り」として 5.4 で廃止している
- ・壁は OS 側に:専用の低権限ユーザー・コンテナ・seccomp・読み取り専用/実行不可のマウント
- ・
disable_functions・open_basedirは、その壁に重ねる保険として使う。単体で隔離の代わりにしない
PHP 本体のメモリ破壊バグそのものへの対処は、結局のところ修正版への更新です。日頃から素早く当てられる仕組みは PHP 緊急アップデートを数分で本番に当てる に、2026年10〜11月に相次いだ PHP 本体の重大な脆弱性は PHP 脆弱性 CVE-2026-109618・109619 にまとめています。設定で面を狭め、OS で壁を作り、バグは更新で塞ぐ——この3つを分けて持つのが、現実的な守り方です。
参照ソース
- PHP: disable_functions(PHP マニュアル・実行ディレクティブ) — php.ini でのみ設定、名前での無効化、バーチャルホスト単位では不可
- PHP: Safe Mode(旧マニュアル・5.3 で非推奨・5.4 で削除) — 「PHP 層で解こうとするのは設計的に誤り」という当時の記述
- PHP: Security(公式のセキュリティ章) — 多層での考え方
- php/php-src(PHP 本体) — 脆弱性の修正は各リリースの CHANGELOG で確認





