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 から戻される
出典: PHP マニュアル(disable_functions は php.ini でのみ設定、名前で無効化)。PHP は safe_mode を『PHP 層で解くのは設計的に誤り』として 5.4 で廃止

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. 1.専用の低権限ユーザーで動かす

    PHP-FPM のプールを、書き込める範囲が狭い専用ユーザーに。root や共有ユーザーで動かさない

  2. 2.コンテナ・名前空間で区切る

    1つのアプリが落ちても他に波及しないよう、プロセス・ファイル・ネットワークを分ける

  3. 3.seccomp で呼べるシステムコールを絞る

    コンテナのランタイムで、不要なシステムコール(新しいプロセスの生成など)を禁止

  4. 4.書き込みと公開範囲を最小に

    アプリの置き場は読み取り専用に。アップロード先は実行不可でマウント。外に出る通信も絞る

出典: 多層防御の一般的な設計。disable_functions は『保険』として併用し、隔離の代わりにはしない
  • 専用の低権限ユーザーで動かす。 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つを分けて持つのが、現実的な守り方です。

参照ソース