はじめに
php.ini を直したのに、設定が効かない。
そもそも cli / fpm / apache2 のどの php.ini を直せばいいのか分からない。
そんな経験はないだろうか。結論を先に書く。
php.ini は SAPI(cli / fpm / apache2)ごとに別。conf.d の断片が php.ini を上書きし、php-fpm では pool 設定がさらに上書きする。編集は php.ini 本体ではなく conf.d(または pool)に置くと、パッケージ更新で壊れない。脆弱性対応の本丸はパッケージ更新で、ini の堅牢化は攻撃面を減らす補助。
この記事で分かること:
-
/etc/php配下のファイル群の役割と、読み込まれる順 - 設定の編集をどこに置き、どう反映するか
- 脆弱性対応・堅牢化で見直す設定
検証環境
- Ubuntu 24.04 の php8.3(8.3.6-0ubuntu0.24.04.13)
- AlmaLinux 9 の php:8.3 ストリーム(8.3.36)
- どちらも Docker コンテナで確認した。出力は加工していない
- Docker 公式の
php:*イメージは配置が別なので、この記事では扱わない
呼び方
- Debian 系:Debian / Ubuntu
- RHEL 系:RHEL / AlmaLinux / Rocky Linux
全体像:Debian 系
まず Debian 系(今回は Ubuntu 24.04)のツリーを見る。
find /etc/php -maxdepth 2 | sort
/etc/php
/etc/php/8.3
/etc/php/8.3/cli
/etc/php/8.3/fpm
/etc/php/8.3/mods-available
👉 -maxdepth 2 で切っているので、php.ini などファイルの中身までは出ない。
/etc/php/<version>/ の下はこう分かれている。
| パス | 役割 |
|---|---|
<version>/cli/php.ini |
CLI(php コマンド)用の php.ini |
<version>/fpm/php.ini |
php-fpm 用の php.ini |
<version>/<sapi>/conf.d/ |
追加の ini 置き場。中身は mods-available/ へのシンボリックリンク |
<version>/mods-available/ |
拡張ごとの ini の実体 |
<version>/fpm/php-fpm.conf |
php-fpm 全体の設定。pool.d/*.conf を include する |
<version>/fpm/pool.d/www.conf |
pool(ワーカーの単位)の設定 |
-
conf.d/の中身は、10-opcache.ini -> /etc/php/8.3/mods-available/opcache.iniのようなmods-available/へのシンボリックリンク -
apache2/は、libapache2-mod-php を入れると現れる。今回は入れていないので上のツリーには無い
👉 SAPI ごとに php.ini が別。php --ini が示すのは CLI の php.ini(/etc/php/8.3/cli/php.ini)で、php-fpm が読むのは /etc/php/8.3/fpm/php.ini。
php.ini-production と php.ini-development は、設定ディレクトリではなく /usr/lib/php/8.3/ にある。
ls /usr/lib/php/8.3/
php.ini-development
php.ini-production
php.ini-production.cli
sapi
全体像:RHEL 系
次に RHEL 系(今回は AlmaLinux 9)。
AlmaLinux 9 の AppStream には php のモジュールストリームが複数ある。今回は 8.3 ストリームを有効にして、Debian 系と同じ 8.3 系に揃えた。
dnf -q module list php
AlmaLinux 9 - AppStream
Name Stream Profiles Summary
php 8.1 common [d], devel, minimal PHP scripting language
php 8.2 common [d], devel, minimal PHP scripting language
php 8.3 [e] common [d], devel, minimal PHP scripting language
Hint: [d]efault, [e]nabled, [x]disabled, [i]nstalled
👉 [e] が付いた 8.3 が有効なストリーム。
ツリーを見る。
ls -la /etc/php.ini /etc/php-fpm.conf /etc/php.d /etc/php-fpm.d
-rw-r--r-- 1 root root 4908 Oct 21 21:25 /etc/php-fpm.conf
-rw-r--r-- 1 root root 62851 Oct 21 21:25 /etc/php.ini
/etc/php-fpm.d:
total 32
drwxr-xr-x 2 root root 4096 Oct 22 05:25 .
drwxr-xr-x 1 root root 4096 Oct 22 05:25 ..
-rw-r--r-- 1 root root 19491 Oct 21 21:25 www.conf
/etc/php.d:
total 56
drwxr-xr-x 2 root root 4096 Oct 22 05:25 .
drwxr-xr-x 1 root root 4096 Oct 22 05:25 ..
-rw-r--r-- 1 root root 44 Oct 21 21:25 20-bz2.ini
-rw-r--r-- 1 root root 54 Oct 21 21:25 20-calendar.ini
-rw-r--r-- 1 root root 48 Oct 21 21:25 20-ctype.ini
-rw-r--r-- 1 root root 46 Oct 21 21:25 20-curl.ini
-rw-r--r-- 1 root root 46 Oct 21 21:25 20-exif.ini
-rw-r--r-- 1 root root 54 Oct 21 21:25 20-fileinfo.ini
-rw-r--r-- 1 root root 44 Oct 21 21:25 20-ftp.ini
-rw-r--r-- 1 root root 52 Oct 21 21:25 20-gettext.ini
-rw-r--r-- 1 root root 48 Oct 21 21:25 20-iconv.ini
-rw-r--r-- 1 root root 46 Oct 21 21:25 20-phar.ini
-rw-r--r-- 1 root root 56 Oct 21 21:25 20-tokenizer.ini
php --ini の結果はこう。
php --ini
Configuration File (php.ini) Path: /etc
Loaded Configuration File: /etc/php.ini
Scan for additional .ini files in: /etc/php.d
Additional .ini files parsed: /etc/php.d/20-bz2.ini,
/etc/php.d/20-calendar.ini,
/etc/php.d/20-ctype.ini,
/etc/php.d/20-curl.ini,
/etc/php.d/20-exif.ini,
/etc/php.d/20-fileinfo.ini,
/etc/php.d/20-ftp.ini,
/etc/php.d/20-gettext.ini,
/etc/php.d/20-iconv.ini,
/etc/php.d/20-phar.ini,
/etc/php.d/20-tokenizer.ini
Debian 系との最大の違いは、php.ini が SAPI 共通で 1 つなこと。
- php.ini は
/etc/php.iniの 1 つだけ。SAPI ごとのファイルは無い - 追加の ini は
/etc/php.d/に直接並ぶ。上のls -laでは通常ファイル(-rw-r--r--)で、mods-availableに相当するものは見当たらない - php-fpm の設定は
/etc/php-fpm.confと/etc/php-fpm.d/
php.ini-production の置き場所は、パッケージのファイル一覧で確認できる。
rpm -ql php-common | grep -E 'php\.ini'
/etc/php.ini
/usr/share/doc/php-common/php.ini-development
/usr/share/doc/php-common/php.ini-production
👉 /etc/php.ini が実際に読まれる設定で、php.ini-production / php.ini-development は /usr/share/doc/php-common/ にある見本。
対応表:Debian 系と RHEL 系
| 役割 | Debian 系(Ubuntu 24.04) | RHEL 系(AlmaLinux 9) |
|---|---|---|
| php.ini |
/etc/php/8.3/cli/php.ini、/etc/php/8.3/fpm/php.ini(SAPI ごと) |
/etc/php.ini(共通で 1 つ) |
| 追加 ini | /etc/php/8.3/<sapi>/conf.d/ |
/etc/php.d/ |
| php-fpm.conf | /etc/php/8.3/fpm/php-fpm.conf |
/etc/php-fpm.conf |
| pool | /etc/php/8.3/fpm/pool.d/www.conf |
/etc/php-fpm.d/www.conf |
| php.ini-production | /usr/lib/php/8.3/php.ini-production |
/usr/share/doc/php-common/php.ini-production |
❌ 「Ubuntu の手順をそのまま AlmaLinux に当てはめる」
👉 パスの形が違う。RHEL 系には <version>/<sapi>/ の階層が無い。
読み込み順と上書きの優先度
設定は 1 か所では決まらない。後から読まれたものが、先に読まれたものを上書きする。
この記事では、優先順位を次の名前で呼ぶ。
php.ini → conf.d → pool(php_admin_value / php_value)→ .user.ini(fpm / CGI)または .htaccess(mod_php)→ ini_set()
👉 右へ行くほど後から読まれる。conf.d は php.ini の後に読まれる追加の ini ファイルで、php.ini の値を上書きできる(第 9 章で、php.ini only の expose_php を conf.d で変えた)。
👉 changeable mode が制限するのは、その後ろの層(pool / .user.ini / ini_set())で変えられるかどうか(後述の表)。
👉 php_admin_value / php_admin_flag で決めた値は ini_set() では変えられない(後述のマニュアルの記述)。.user.ini や .htaccess との優先関係は、この記事では扱わない。
conf.d はアルファベット順に読まれる
PHP マニュアルには次のとおり書かれている。
After the primary configuration file step, PHP may additionally scan for
.inifiles in the path specified by the PHP_INI_SCAN_DIR environment variable, or, if that is not set, by the --with-config-file-scan-dir compile time option.Within each directory, PHP will scan all files ending in
.iniin alphabetical order.
- php.ini を読んだ後に、スキャンディレクトリ(conf.d)の
.iniを読む - ファイル名のアルファベット順に読む。だから
10-20-と数字の接頭辞が付く - 後から読まれる
99-local.iniのようなファイルが勝つ
changeable mode:どこで変えられるか
設定ごとに「どこで変えられるか」が決まっている。マニュアルの定義はこう。
| mode | どこで変えられるか |
|---|---|
PHP_INI_USER |
ユーザースクリプト(ini_set() など)と .user.ini
|
PHP_INI_PERDIR |
php.ini、.htaccess、httpd.conf、.user.ini
|
PHP_INI_SYSTEM |
php.ini と httpd.conf |
PHP_INI_ALL |
どこでも |
👉 マニュアルでは PHP_ 接頭辞なしの INI_USER などで書かれている。この記事は PHP_INI_* で統一する。
👉 ディレクティブによっては、マニュアルの一覧で mode 欄が "php.ini only" と書かれている(第 7 章で出てくる expose_php と disable_functions)。ini_set() や .user.ini では変えられない。conf.d には書ける(第 9 章)。
.user.ini と .htaccess
-
.user.iniは CGI / FastCGI SAPI だけが処理する。php-fpm もこれに当たる - 認識されるのは
PHP_INI_ALL/PHP_INI_PERDIR/PHP_INI_USERの設定だけ。PHP_INI_SYSTEMは置けない - mod_php では
.htaccessを使う(上のPHP_INI_PERDIRの定義に.htaccessがある)
These files are processed only by the CGI/FastCGI SAPI.
👉 ファイル名は user_ini.filename(既定 .user.ini)、再読込の間隔は user_ini.cache_ttl(既定 300 秒)。変更の反映が最大 300 秒遅れることがある。
php-fpm の pool:php_value と php_admin_value
pool に書く php_value と php_admin_value は、ini_set() で上書きできるかが違う。
Settings defined with
php_admin_valueandphp_admin_flagcannot be overridden with ini_set().
-
php_value/php_flag:以前の値を上書きする。changeable mode がPHP_INI_ALLなどでini_set()を許す設定なら、ini_set()で再び変えられる(前述の mode の表) -
php_admin_value/php_admin_flag:ini_set()で上書きできない - 例外として、
disable_functionsを定義しても php.ini の値は上書きされず、追記される(同じページに記載)
👉 アプリに変えさせたくない値は php_admin_value に書く。
確認する:CLI と fpm は別の php.ini を読む
まず CLI。
php --ini
Configuration File (php.ini) Path: /etc/php/8.3/cli
Loaded Configuration File: /etc/php/8.3/cli/php.ini
Scan for additional .ini files in: /etc/php/8.3/cli/conf.d
Additional .ini files parsed: /etc/php/8.3/cli/conf.d/10-mysqlnd.ini,
/etc/php/8.3/cli/conf.d/10-opcache.ini,
/etc/php/8.3/cli/conf.d/10-pdo.ini,
/etc/php/8.3/cli/conf.d/20-calendar.ini,
/etc/php/8.3/cli/conf.d/20-ctype.ini,
/etc/php/8.3/cli/conf.d/20-exif.ini,
/etc/php/8.3/cli/conf.d/20-ffi.ini,
/etc/php/8.3/cli/conf.d/20-fileinfo.ini,
/etc/php/8.3/cli/conf.d/20-ftp.ini,
/etc/php/8.3/cli/conf.d/20-gettext.ini,
/etc/php/8.3/cli/conf.d/20-iconv.ini,
/etc/php/8.3/cli/conf.d/20-mysqli.ini,
/etc/php/8.3/cli/conf.d/20-pdo_mysql.ini,
/etc/php/8.3/cli/conf.d/20-phar.ini,
/etc/php/8.3/cli/conf.d/20-posix.ini,
/etc/php/8.3/cli/conf.d/20-readline.ini,
/etc/php/8.3/cli/conf.d/20-shmop.ini,
/etc/php/8.3/cli/conf.d/20-sockets.ini,
/etc/php/8.3/cli/conf.d/20-sysvmsg.ini,
/etc/php/8.3/cli/conf.d/20-sysvsem.ini,
/etc/php/8.3/cli/conf.d/20-sysvshm.ini,
/etc/php/8.3/cli/conf.d/20-tokenizer.ini
続いて php-fpm。
php-fpm8.3 -i | grep -E 'Loaded Configuration|Scan this dir|Additional .ini'
Loaded Configuration File => /etc/php/8.3/fpm/php.ini
Scan this dir for additional .ini files => /etc/php/8.3/fpm/conf.d
Additional .ini files parsed => /etc/php/8.3/fpm/conf.d/10-mysqlnd.ini,
👉 Additional .ini files parsed が 1 行目しか出ていないのは、grep が 1 行ずつ拾うため。
❌ 「php -i の結果 = Web で動いている設定」
👉 php は CLI の /etc/php/8.3/cli/php.ini を読む。php-fpm が読むのは /etc/php/8.3/fpm/php.ini。パスも conf.d も別。
確認コマンドの使い分け:
-
php --ini:CLI が読む php.ini と conf.d の一覧 -
php -i:CLI の設定全体 -
php-fpm8.3 -i:php-fpm が読む設定。上のようにgrepで絞る - Web 経由の
phpinfo():実際にリクエストを処理するプロセスの設定
Debian 系の拡張モジュール管理
Debian 系の conf.d は、mods-available/ へのシンボリックリンクが並ぶ。
ls -la /etc/php/8.3/fpm/conf.d
total 8
drwxr-xr-x 2 root root 4096 Oct 22 05:24 .
drwxr-xr-x 4 root root 4096 Oct 22 05:24 ..
lrwxrwxrwx 1 root root 39 Oct 22 05:24 10-mysqlnd.ini -> /etc/php/8.3/mods-available/mysqlnd.ini
lrwxrwxrwx 1 root root 39 Oct 22 05:24 10-opcache.ini -> /etc/php/8.3/mods-available/opcache.ini
lrwxrwxrwx 1 root root 35 Oct 22 05:24 10-pdo.ini -> /etc/php/8.3/mods-available/pdo.ini
lrwxrwxrwx 1 root root 40 Oct 22 05:24 20-calendar.ini -> /etc/php/8.3/mods-available/calendar.ini
lrwxrwxrwx 1 root root 37 Oct 22 05:24 20-ctype.ini -> /etc/php/8.3/mods-available/ctype.ini
lrwxrwxrwx 1 root root 36 Oct 22 05:24 20-exif.ini -> /etc/php/8.3/mods-available/exif.ini
lrwxrwxrwx 1 root root 35 Oct 22 05:24 20-ffi.ini -> /etc/php/8.3/mods-available/ffi.ini
lrwxrwxrwx 1 root root 40 Oct 22 05:24 20-fileinfo.ini -> /etc/php/8.3/mods-available/fileinfo.ini
lrwxrwxrwx 1 root root 35 Oct 22 05:24 20-ftp.ini -> /etc/php/8.3/mods-available/ftp.ini
lrwxrwxrwx 1 root root 39 Oct 22 05:24 20-gettext.ini -> /etc/php/8.3/mods-available/gettext.ini
lrwxrwxrwx 1 root root 37 Oct 22 05:24 20-iconv.ini -> /etc/php/8.3/mods-available/iconv.ini
lrwxrwxrwx 1 root root 38 Oct 22 05:24 20-mysqli.ini -> /etc/php/8.3/mods-available/mysqli.ini
lrwxrwxrwx 1 root root 41 Oct 22 05:24 20-pdo_mysql.ini -> /etc/php/8.3/mods-available/pdo_mysql.ini
lrwxrwxrwx 1 root root 36 Oct 22 05:24 20-phar.ini -> /etc/php/8.3/mods-available/phar.ini
lrwxrwxrwx 1 root root 37 Oct 22 05:24 20-posix.ini -> /etc/php/8.3/mods-available/posix.ini
lrwxrwxrwx 1 root root 40 Oct 22 05:24 20-readline.ini -> /etc/php/8.3/mods-available/readline.ini
lrwxrwxrwx 1 root root 37 Oct 22 05:24 20-shmop.ini -> /etc/php/8.3/mods-available/shmop.ini
lrwxrwxrwx 1 root root 39 Oct 22 05:24 20-sockets.ini -> /etc/php/8.3/mods-available/sockets.ini
lrwxrwxrwx 1 root root 39 Oct 22 05:24 20-sysvmsg.ini -> /etc/php/8.3/mods-available/sysvmsg.ini
lrwxrwxrwx 1 root root 39 Oct 22 05:24 20-sysvsem.ini -> /etc/php/8.3/mods-available/sysvsem.ini
lrwxrwxrwx 1 root root 39 Oct 22 05:24 20-sysvshm.ini -> /etc/php/8.3/mods-available/sysvshm.ini
lrwxrwxrwx 1 root root 41 Oct 22 05:24 20-tokenizer.ini -> /etc/php/8.3/mods-available/tokenizer.ini
👉 各行が 10-opcache.ini -> /etc/php/8.3/mods-available/opcache.ini の形。実体は mods-available にある。
接頭辞の数字は、mods-available の ini にあるコメント ; priority= に対応している。同じ環境で 2 つの ini を見るとこうなる。
cat /etc/php/8.3/mods-available/opcache.ini /etc/php/8.3/mods-available/pdo_mysql.ini
; configuration for php opcache module
; priority=10
zend_extension=opcache.so
opcache.jit=off
; configuration for php mysql module
; priority=20
extension=pdo_mysql.so
👉 priority=10 が 10-opcache.ini、priority=20 が 20-pdo_mysql.ini に対応する。
phpenmod / phpdismod / phpquery
拡張の有効・無効は、リンクを手で張らずにコマンドで行う。phpenmod を引数なしで実行すると usage が出る。
phpenmod 2>&1 | head -20
WARNING:
usage: phpenmod [ -v ALL|php_version ] [ -s ALL|sapi_name ] module_name [ module_name_2 ]
-
phpenmod:モジュールを有効にする -
phpdismod:モジュールを無効にする -
phpquery:モジュールの状態を調べる
オプションは 1 行ずつ。
-
-v:PHP のバージョンを指定する(phpenmodではALLも可) -
-s:SAPI を指定する(phpenmodではALLも可) -
-m:モジュール名を指定して状態を見る(phpquery。例:phpquery -v 8.3 -s fpm -m opcache) -
-M:有効なモジュールの一覧を出す(phpquery。今回の環境では上の conf.d のリンクと同じ 22 個だったので、出力は載せない)
phpquery を引数なしで実行すると usage が出る。モジュール名を指定すると状態が見える。
phpquery 2>&1
WARNING: You need to specify at least one action.
usage: phpquery [ -d ] [ -q ] -v version_name -s sapi_name [ -m module_name ] [ -M ] [ -S ] [ -V ]
phpquery -v 8.3 -s fpm -m opcache
opcache (Enabled for fpm by maintainer script)
複数バージョンが入っているとき
PHP を複数バージョン入れている場合、php コマンドの向き先は update-alternatives で切り替える。
下は今回の環境で update-alternatives --display php を実行した出力。8.3 しか入れていないので、候補は 1 つだけ。切り替えの操作そのものは、この環境では実行していない。
update-alternatives --display php
php - auto mode
link best version is /usr/bin/php8.3
link currently points to /usr/bin/php8.3
link php is /usr/bin/php
slave php.1.gz is /usr/share/man/man1/php.1.gz
/usr/bin/php8.3 - priority 83
slave php.1.gz: /usr/share/man/man1/php8.3.1.gz
👉 複数バージョンが並んだ出力ではない。
RHEL 系は直接並ぶ
RHEL 系は /etc/php.d/*.ini に通常ファイルが直接並ぶ(第 3 章の ls -la の出力)。
👉 自分の設定の置き場所(詳細は第 9 章):
- Debian 系:
mods-available/に ini を置いてphpenmodするか、<sapi>/conf.d/に99-local.iniを置く - RHEL 系:
/etc/php.d/99-local.iniを置く
php-fpm.conf と pool
php-fpm の設定は 2 層に分かれる。
-
php-fpm.conf:php-fpm 全体(グローバル)の設定 - pool:ワーカーの単位。
userやlistenは pool ごとに決める
まず Debian 系のグローバル設定。コメントと空行を除いて見る。
grep -vE '^\s*(;|$)' /etc/php/8.3/fpm/php-fpm.conf
[global]
pid = /run/php/php8.3-fpm.pid
error_log = /var/log/php8.3-fpm.log
include=/etc/php/8.3/fpm/pool.d/*.conf
👉 最終行の include= が pool.d/*.conf を読み込んでいる。pool の設定ファイルを足すだけで pool が増える仕組みは、この実ファイルが根拠。
次に pool(www.conf)。
grep -vE '^\s*(;|$)' /etc/php/8.3/fpm/pool.d/www.conf
[www]
user = www-data
group = www-data
listen = /run/php/php8.3-fpm.sock
listen.owner = www-data
listen.group = www-data
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
👉 [www] が pool 名。以降のキーはその pool だけに効く。
主要キー
| キー | 置き場所 | 役割(出典の範囲) |
|---|---|---|
pid |
グローバル | pid ファイルの場所。既定値は書かない。Debian 系の実際の値は上の出力のとおり |
error_log |
グローバル | php-fpm 自体のログ。既定は #INSTALL_PREFIX#/log/php-fpm.log、syslog も指定できる |
emergency_restart_threshold |
グローバル | この数の子プロセスが、emergency_restart_interval の間に SIGSEGV / SIGBUS で終了すると FPM が再起動する。既定は 0(Off) |
process_control_timeout |
グローバル | 既定は 0 |
include |
グローバル | 上の php-fpm.conf では pool.d/*.conf を指している(実ファイルで確認) |
user / group
|
pool | ワーカーの実行ユーザー。user は必須。group は未設定なら既定ユーザーのグループ |
listen |
pool | FastCGI リクエストを受ける場所。pool ごとに必須。ip.add.re.ss:port、port、UNIX ソケットのパスが書ける |
listen.owner / listen.mode
|
pool | UNIX ソケットのパーミッション。既定は実行ユーザーとグループ、mode は 0660 |
pm |
pool | 必須。static / ondemand / dynamic
|
pm.* |
pool | プロセス数の設定。チューニングはこの記事の範囲外 |
php_admin_value / php_admin_flag
|
pool |
ini_set() で上書きできない |
php_value / php_flag
|
pool | 以前の値を上書きする |
security.limit_extensions |
pool | FPM が解釈するメインスクリプトの拡張子。既定は .php .phar
|
request_terminate_timeout |
pool | 1 リクエストの処理時間がこれを超えるとワーカーを終了する。既定は 0(Off) |
slowlog |
pool | スローログの出力先。既定は #INSTALL_PREFIX#/log/php-fpm.log.slow
|
👉 pid と pm の既定値は書かない。Debian 系・RHEL 系の実際の値は、実ファイルの出力で示した。
👉 disable_functions を php_value で定義しても php.ini の値は上書きされず、追記される(同じページに記載)。
listen は UNIX ソケット
Debian 系の listen は、パスで書かれている。
ls -la /run/php/ 2>&1; grep -nE '^listen' /etc/php/8.3/fpm/pool.d/www.conf
total 12
drwxr-xr-x 2 www-data www-data 4096 Oct 22 05:24 .
drwxr-xr-x 1 root root 4096 Oct 22 05:24 ..
41:listen = /run/php/php8.3-fpm.sock
53:listen.owner = www-data
54:listen.group = www-data
👉 listen の値がファイルのパスなら UNIX ソケット。この出力にはソケットファイル自体は出ていない。確認できたのは listen の値と、/run/php/ の所有者が www-data なこと。
Web サーバーはこのソケットに接続する。マニュアルにはこうある。
In Linux, read/write permissions must be set in order to allow connections from a web server.
- Web サーバーのユーザーが読み書きできるように、
listen.owner/listen.group/listen.modeで合わせる - 上の出力では
listen.owner = www-data、listen.group = www-data。mode は既定の 0660
RHEL 系の pool
RHEL 系の pool は /etc/php-fpm.d/www.conf。同じ方法で見る。
grep -vE '^\s*(;|$)' /etc/php-fpm.d/www.conf
[www]
user = apache
group = apache
listen = /run/php-fpm/www.sock
listen.acl_users = apache,nginx
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 50
pm.start_servers = 5
pm.min_spare_servers = 5
pm.max_spare_servers = 35
slowlog = /var/log/php-fpm/www-slow.log
php_admin_value[error_log] = /var/log/php-fpm/www-error.log
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files
php_value[session.save_path] = /var/lib/php/session
php_value[soap.wsdl_cache_dir] = /var/lib/php/wsdlcache
Debian 系との差:
-
user/groupはapache(Debian 系はwww-data) -
listenは/run/php-fpm/www.sock(Debian 系は/run/php/php8.3-fpm.sock) -
listen.owner/listen.groupの代わりにlisten.acl_users = apache,nginxとlisten.allowed_clients = 127.0.0.1がある -
pm.max_childrenなどの値が大きい(50 / 5 / 5 / 35) -
slowlog、php_admin_value[error_log]、php_value[session.save_path]などが最初から入っている
👉 listen.acl_users は、POSIX ACL でソケットに接続できるユーザーを指定する。マニュアルには "When set, listen.owner and listen.group are ignored." とある。RHEL 系の www.conf に listen.owner が無いのは、これを使っているため。
👉 listen.allowed_clients は、接続を許す FastCGI クライアントの IP アドレス。マニュアルには "Makes sense only with a tcp listening socket." とある。この pool は UNIX ソケットで待ち受けているので、この行は意味を持たない。
独自 pool の設定例
www.conf を直さず、新しいファイルを pool.d/(RHEL 系は php-fpm.d/)に足す。www.conf を直接編集しないほうが、自分の変更を差分として管理しやすい。
以下は例(/etc/php/8.3/fpm/pool.d/app.conf)。
[app]
user = app
group = app
listen = /run/php/app.sock
listen.owner = www-data
listen.group = www-data
listen.mode = 0660
pm = dynamic
pm.max_children = 5
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
php_admin_value[disable_functions] = exec,passthru,shell_exec,system,proc_open,popen
php_admin_flag[display_errors] = off
👉 listen は UNIX ソケットのパス。TCP で待ち受ける場合は listen = 127.0.0.1:<port> とし、listen.allowed_clients = 127.0.0.1 を添える。マニュアルは、公開された FastCGI の待ち受けは任意コード実行を許すと警告し、listen.allowed_clients は空のままだと "connections will be accepted from any ip address" と書いている(install.fpm.configuration.php)。
👉 pool の php_admin_value / php_admin_flag で設定した値は、ini_set() で上書きできない(install.fpm.configuration.php)。disable_functions は pool に書くと php.ini の値に追記される(install.fpm.configuration.php)。
構文確認と反映
設定を足したら、まず構文を確認する。
php-fpm8.3 -t
[22-Oct-2026 05:25:56] NOTICE: configuration file /etc/php/8.3/fpm/php-fpm.conf test is successful
問題なければ反映する。Debian 系:
systemctl reload php8.3-fpm
RHEL 系:
systemctl reload php-fpm
👉 reload は設定を読み直す。出力は出ないので、反映の確認は第 9 章の方法で行う。
脆弱性対応・堅牢化で見直す ini パラメータ
各項目は「何が起きるか → 設定値 → どこに書くか」の順で書く。
- 「changeable mode」は、第 4 章の
PHP_INI_*の表記。マニュアルの一覧(ini.list.php)に書かれた値をそのまま使う - 「既定値」は PHP 本体の既定。ディストリビューションの php.ini や
php.ini-productionの値とは別 - 設定値は「例」。アプリの要件に合わせて決める
まず php.ini-production との差を見る
Debian 系の見本は 2 つある。php.ini-production と php.ini-development で、関係する行を並べる。
grep -nE '^(display_errors|log_errors|expose_php|allow_url_include|allow_url_fopen|session\.cookie_httponly|session\.use_strict_mode|session\.cookie_samesite|session\.upload_progress\.enabled|memory_limit|post_max_size|disable_functions)\b' /usr/lib/php/8.3/php.ini-production /usr/lib/php/8.3/php.ini-development
/usr/lib/php/8.3/php.ini-production:333:disable_functions =
/usr/lib/php/8.3/php.ini-production:410:expose_php = Off
/usr/lib/php/8.3/php.ini-production:445:memory_limit = 128M
/usr/lib/php/8.3/php.ini-production:518:display_errors = Off
/usr/lib/php/8.3/php.ini-production:537:log_errors = On
/usr/lib/php/8.3/php.ini-production:713:post_max_size = 8M
/usr/lib/php/8.3/php.ini-production:876:allow_url_fopen = On
/usr/lib/php/8.3/php.ini-production:880:allow_url_include = Off
/usr/lib/php/8.3/php.ini-production:1393:session.use_strict_mode = 0
/usr/lib/php/8.3/php.ini-production:1432:session.cookie_httponly =
/usr/lib/php/8.3/php.ini-production:1438:session.cookie_samesite =
/usr/lib/php/8.3/php.ini-development:339:disable_functions =
/usr/lib/php/8.3/php.ini-development:414:expose_php = On
/usr/lib/php/8.3/php.ini-development:449:memory_limit = 128M
/usr/lib/php/8.3/php.ini-development:522:display_errors = On
/usr/lib/php/8.3/php.ini-development:541:log_errors = On
/usr/lib/php/8.3/php.ini-development:717:post_max_size = 8M
/usr/lib/php/8.3/php.ini-development:880:allow_url_fopen = On
/usr/lib/php/8.3/php.ini-development:884:allow_url_include = Off
/usr/lib/php/8.3/php.ini-development:1397:session.use_strict_mode = 0
/usr/lib/php/8.3/php.ini-development:1436:session.cookie_httponly =
/usr/lib/php/8.3/php.ini-development:1442:session.cookie_samesite =
- この出力の範囲では、production と development で値が違うのは
expose_php(Off / On)とdisplay_errors(Off / On) -
session.cookie_httponlyとsession.cookie_samesiteは、どちらも値が空 -
session.use_strict_modeは、どちらも 0
👉 production を基準にして、自分の変更は conf.d の 99-local.ini に足す。
expose_php
マニュアルの説明:
Exposes to the world that PHP is installed on the server, which includes the PHP version within the HTTP header (e.g., X-Powered-By: PHP/5.3.7).
expose_php = Off
- 既定値は
"1"、changeable mode は "php.ini only" -
php.ini-productionではOff(development はOn) -
ini_set()や.user.iniでは変えられない。conf.d の99-local.iniに書く - conf.d に書いて効くことは、CLI で確認した(第 9 章)
👉 Web サーバー側でヘッダーを消す方法は、別の記事にまとめた。
https://qiita.com/gtakairo/items/2a7e08c618f802ca3b23
display_errors / log_errors / error_log
-
display_errors:既定"1"、PHP_INI_ALL -
log_errors:既定"0"、PHP_INI_ALL -
error_log:既定null、PHP_INI_ALL -
php.ini-productionはdisplay_errors = Off、log_errors = On(development はdisplay_errors = On)
本番はエラーを画面に出さず、ログに残す。
display_errors = Off
log_errors = On
error_log = /var/log/php/app-error.log
👉 3 つとも PHP_INI_ALL なので、ini_set() でも変えられる。pool の php_admin_value / php_admin_flag で設定した値は、ini_set() で上書きできない(install.fpm.configuration.php)。RHEL 系の www.conf には php_admin_value[error_log] と php_admin_flag[log_errors] が最初から入っていた。
allow_url_include と allow_url_fopen
2 つは別の設定。
-
allow_url_fopen:マニュアルの説明は "This option enables the URL-aware fopen wrappers that enable accessing URL object like files."。既定"1"、PHP_INI_SYSTEM -
allow_url_include:マニュアルの説明は "This option allows the use of URL-aware fopen wrappers with the following functions: include, include_once, require, require_once."。既定"0"、PHP_INI_SYSTEM。7.4.0 で非推奨 -
allow_url_includeには "This setting requires allow_url_fopen to be on." という注記がある
allow_url_include = Off
👉 php.ini-production は allow_url_include = Off、allow_url_fopen = On。
👉 どちらも PHP_INI_SYSTEM なので、ini_set() と .user.ini では変えられない。conf.d に書く。
disable_functions
マニュアルの記述:
- カンマ区切りの関数名を書く
- 内部関数だけが対象
- 8.0.0 以降は、無効化した関数の定義が消え、ユーザーランドで再定義できる
- php.ini にだけ設定でき、httpd.conf には書けない(changeable mode は "php.ini only")
- 既定値は
""
This directive can be circumvented and should not be considered a sufficient security measure for shared hosting environments.
例:
disable_functions = exec,passthru,shell_exec,system,proc_open,popen
- pool の
php_value/php_admin_valueでdisable_functionsを定義すると、php.ini の値は上書きされず追記される(install.fpm.configuration.php) - 副作用:アプリが使う関数を切ると、そのアプリは動かなくなる。入れる前に、使っている関数を確認する
- 書く場所は conf.d の
99-local.ini。conf.d の ini は、php.ini の後に読まれる追加の ini ファイル(第 4 章の "After the primary configuration file step")。同じ php.ini only のexpose_phpは、conf.d で上書きできた(第 9 章) - disable_functions 自体は本記事では実証していない
👉 マニュアル自身が「回避され得る」「十分なセキュリティ対策とは見なすべきでない」と書いている。入口を減らす補助として使う。
open_basedir
PHP がアクセスできるファイルを、指定したディレクトリツリーに限る設定。既定 null、PHP_INI_ALL。
open_basedir = /var/www/app:/tmp
👉 上は例。パスはアプリに合わせる。
マニュアルに書かれている制限と限界:
- シンボリックリンクはすべて解決される。symlink では、この制限を回避できない
- 実行時に
ini_set()で狭めることはできる - 8.3.0 以降は、実行時に
..を含むパスを受け付けない - Unix ドメインソケットへのアクセスは制限しない。
stream_socket_client()やfsockopen()で接続できる -
.(作業ディレクトリ)はchdir()で変わるので、少し危険 - 有効にすると
realpath_cache_sizeが 0 になり、キャッシュが無効になる
open_basediris just an extra safety net, that is in no way comprehensive, and can therefore not be relied upon when security is needed.
❌ 「open_basedir を設定すれば、ほかのディレクトリは見られない」
👉 マニュアル自身が、包括的ではなく、セキュリティが必要なときに頼ってはいけないと書いている。
session.cookie_httponly / session.cookie_secure / session.use_strict_mode / session.cookie_samesite
4 つとも PHP_INI_ALL。PHP 本体の既定値と、マニュアルの説明は次のとおり。
| パラメータ | 既定値 | 意味 |
|---|---|---|
session.cookie_httponly |
"0" |
クッキーを HTTP プロトコル経由だけで参照できるようにする。JavaScript などのスクリプトからは見えなくなる |
session.cookie_secure |
"0" |
クッキーを安全な接続でだけ送るか。On にするとセッションは HTTPS 接続でしか動かない |
session.use_strict_mode |
"0" |
未初期化のセッション ID を受け付けない。ブラウザから未初期化の ID が送られると、新しい ID を返す |
session.cookie_samesite |
""(7.3.0 以降) |
クロスサイトのリクエストにクッキーを付けないよう指示する。空なら SameSite 属性を付けない。Lax と Strict はクロスドメインの POST に付けない。クロスドメインの GET には Lax は付け、Strict は付けない |
https://www.php.net/manual/en/ini.list.php
https://www.php.net/manual/en/session.configuration.php
👉 session.cookie_secure を 1 にすると HTTP 接続ではセッションが動かない。HTTPS で動かしているアプリに限って入れる。
見直しの例:
session.cookie_httponly = 1
session.cookie_secure = 1
session.use_strict_mode = 1
session.cookie_samesite = Lax
- 上は例。
Laxは、cookie_samesiteに指定できる値の 1 つ -
php.ini-productionのsession.use_strict_modeは 0、session.cookie_httponlyとsession.cookie_samesiteは空。production を基準にしても、この 3 つは自分で決める必要がある
👉 PHP_INI_ALL なので ini_set() でも変えられる。pool の php_admin_value / php_admin_flag で設定した値は、ini_set() で上書きできない(install.fpm.configuration.php)。
cgi.fix_pathinfo と security.limit_extensions
cgi.fix_pathinfo は、CGI の PATH_INFO / PATH_TRANSLATED を仕様に合わせる設定。
It is turned on by default.
- 既定
"1"、PHP_INI_SYSTEM -
ini_set()では変えられない。conf.d に書く
security.limit_extensions は pool の設定。FPM が解釈するメインスクリプトの拡張子を絞る。
You should only limit FPM to .php extensions to prevent malicious users to use other extensions to execute php code.
- 既定値は
.php .phar - Web サーバー側の設定ミスを防ぐ目的で使える、とマニュアルにある
security.limit_extensions = .php
👉 FPM 側で実行する拡張子を絞る、という位置づけ。.phar を使わないなら .php だけにする例。
👉 cgi.fix_pathinfo と Nginx の関係は、php.net の原文に記述が無い。この記事では扱わない。
file_uploads / upload_max_filesize / post_max_size / max_input_vars / memory_limit
受け付ける入力の上限と、メモリの上限。memory_limit のマニュアルの説明は "This sets the maximum amount of memory in bytes that a script is allowed to allocate."(https://www.php.net/manual/en/ini.core.php)。
| パラメータ | 既定値 | changeable mode |
|---|---|---|
file_uploads |
"1" |
PHP_INI_SYSTEM |
upload_max_filesize |
"2M" |
PHP_INI_PERDIR |
post_max_size |
"8M" |
PHP_INI_PERDIR |
max_input_vars |
1000 |
PHP_INI_PERDIR |
memory_limit |
"128M" |
PHP_INI_ALL |
file_uploads = Off
upload_max_filesize = 2M
post_max_size = 8M
max_input_vars = 1000
memory_limit = 128M
👉 上は例。ファイルアップロードを使わないアプリなら file_uploads = Off。使うなら、必要な大きさに合わせる。
👉 php.ini-production の memory_limit と post_max_size は、既定と同じ 128M と 8M。
👉 memory_limit / post_max_size は、第 8 章でも出てくる。第 8 章で示すのは 2026-10-21 の公式アドバイザリ向けの別の設定で、ここの例(128M / 8M)とは目的が違う。
session.upload_progress.enabled
既定 "1"、changeable mode は PHP_INI_PERDIR。
session.upload_progress.enabled = 0
-
PHP_INI_PERDIRなので、ini_set()では変えられない - conf.d か pool に書く
- 書いたあと、
reloadで足りるか再起動が要るかは、この記事では検証していない。公式アドバイザリの記述は第 8 章で扱う
👉 この設定は、第 8 章の 2026-10-21 の公式アドバイザリでも暫定策として出てくる。暫定策だけでは安全にならず、完全な対処は更新のみ、という公式の記述と合わせて第 8 章で書く。
enable_dl
- 既定
"1"、changeable mode はPHP_INI_SYSTEM - マニュアルの注記は、"This deprecated feature will certainly be removed in the future."
enable_dl = 0
👉 非推奨で、将来削除される機能。上は例。PHP_INI_SYSTEM なので conf.d に書く。動作の説明は、この記事では扱わない。
一覧表
| パラメータ | 推奨値(例) | 書く場所 | changeable mode |
|---|---|---|---|
expose_php |
Off |
conf.d の 99-local.ini(CLI で効くことを第 9 章で確認) |
php.ini only |
display_errors |
Off |
conf.d の 99-local.ini(変えさせたくなければ pool の php_admin_flag) |
PHP_INI_ALL |
log_errors |
On |
conf.d の 99-local.ini(または pool の php_admin_flag) |
PHP_INI_ALL |
error_log |
ログのパス | conf.d の 99-local.ini(または pool の php_admin_value) |
PHP_INI_ALL |
allow_url_include |
Off |
conf.d の 99-local.ini
|
PHP_INI_SYSTEM |
allow_url_fopen |
アプリが使わないなら Off
|
conf.d の 99-local.ini
|
PHP_INI_SYSTEM |
disable_functions |
アプリが使わない関数だけ | conf.d の 99-local.ini(本記事では未実証。pool の php_admin_value は追記) |
php.ini only |
open_basedir |
アプリのディレクトリ | conf.d の 99-local.ini(または pool の php_admin_value) |
PHP_INI_ALL |
session.cookie_httponly |
1 |
conf.d の 99-local.ini(または pool の php_admin_flag) |
PHP_INI_ALL |
session.cookie_secure |
1 |
conf.d の 99-local.ini(または pool の php_admin_flag) |
PHP_INI_ALL |
session.use_strict_mode |
1 |
conf.d の 99-local.ini(または pool の php_admin_flag) |
PHP_INI_ALL |
session.cookie_samesite |
Lax |
conf.d の 99-local.ini(または pool の php_admin_value) |
PHP_INI_ALL |
cgi.fix_pathinfo |
既定値 1 のまま(変更する根拠は本記事では扱わない) |
conf.d の 99-local.ini
|
PHP_INI_SYSTEM |
security.limit_extensions |
.php |
pool | 表に無い(pool の設定) |
file_uploads |
使わなければ Off
|
conf.d の 99-local.ini
|
PHP_INI_SYSTEM |
upload_max_filesize |
2M |
conf.d の 99-local.ini
|
PHP_INI_PERDIR |
post_max_size |
8M |
conf.d の 99-local.ini
|
PHP_INI_PERDIR |
max_input_vars |
1000 |
conf.d の 99-local.ini
|
PHP_INI_PERDIR |
memory_limit |
128M |
conf.d の 99-local.ini
|
PHP_INI_ALL |
session.upload_progress.enabled |
0 |
conf.d の 99-local.ini または pool |
PHP_INI_PERDIR |
enable_dl |
0 |
conf.d の 99-local.ini
|
PHP_INI_SYSTEM |
👉 「conf.d の 99-local.ini」は、Debian 系は /etc/php/8.3/fpm/conf.d/、RHEL 系は /etc/php.d/。
👉 ini は攻撃面を減らす補助。脆弱性対応の本丸はパッケージ更新(第 8 章)。
脆弱性対応の本丸はパッケージ更新
ini の堅牢化は、攻撃面を減らす補助。脆弱性対応の本丸はパッケージ更新。
設定で塞げるのは、特定の経路だけ。コードの欠陥そのものは、更新で直すしかない。
例 1:php2shell(CVE-2026-109618 / CVE-2026-109619)
「php2shell」は 2 件をまとめて呼ぶ通称で、公式のアドバイザリ(php.net の告知と GHSA)はこの名前を使っていない。
2026-10-21 に、PHP 公式が 2 件の critical な脆弱性の修正版を出した。
- 修正版:8.5.12 / 8.4.27 / 8.3.36 / 8.2.35
- 2 件とも、影響を受けるのは
< 8.2.35、< 8.3.36、< 8.4.27、< 8.5.12 - CVE-2026-109618:メモリ不足(OOM)の処理に起因する
- CVE-2026-109619:古いグローバル状態が残ることに起因する。確認済みの経路は、初期化前の INI 診断とセッションのアップロード進捗
https://www.php.net/archive/2026.php
https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w
https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp
公式の告知は、すぐ更新できない場合の暫定策を挙げている。ただし、原文はこう書いている。
If you cannot update immediately: while only an update is a complete solution, the following measures reduce exposure to specific attack paths but do not make an installation safe.
👉 暫定策は、特定の攻撃経路への露出を減らすだけ。導入環境を安全にはしない(原文: do not make an installation safe)。完全な解決は更新のみ。
告知が挙げている暫定策は 3 つ。
-
memory_limit=-1。アプリが上書きできない形(FPM pool のphp_admin_valueや Apache の設定)で。どこかで有限の値に戻していないかも確認するThis removes the controllable PHP memory limit failure, but an underlying allocation failure can still reach the vulnerable path.
-
post_max_sizeを低くする。アップロード不要のアプリなら8M、必要なら32M。upload_max_filesizeはpost_max_sizeより小さくする -
session.upload_progress.enabled=0にして、全ワーカーを再起動するThis blocks the confirmed session paths but not other variants.
最後の項目には、こうも添えられている。
Do not rely on
ini_set()or FPM.user.ini.
👉 第 7 章の表で見たとおり、session.upload_progress.enabled は PHP_INI_PERDIR。ini_set() では変えられない。PHP_INI_PERDIR は .user.ini にも書ける(第 4 章の表)が、告知は頼るなと書いている。php.ini、conf.d、pool のように、リクエストの解析より前に効く場所に書く。
conf.d の 99-local.ini に書く形は、こうなる。以下は例。
; 例:php2shell の暫定策。これだけでは導入環境は安全にならない。更新が先
session.upload_progress.enabled = 0
post_max_size = 8M
upload_max_filesize = 2M
memory_limit=-1 は、アプリに戻させない形にする。memory_limit は PHP_INI_ALL なので、conf.d に書くだけだと ini_set() で変えられてしまう。pool に書く例(これも例):
php_admin_value[memory_limit] = -1
👉 memory_limit = -1 は上限なしの値。第 7 章の例(128M)とは目的が違う。公式の暫定策に従うときだけの値で、更新したら元に戻す判断をする。
👉 書いたあとに reload で足りるか再起動が要るかは、この記事では検証していない。公式の告知は「全ワーカーを再起動」と書いている。
❌ 「multipart のリクエストを弾けば安全」
👉 GHSA-w8hw-w9fx-j79w は、次のように書いている。
Several multipart parsing problems can let a request reach OOM while remaining within its apparent PHP limits:
multipart は OOM に至る経路の一部で、必須条件とは書かれていない。同じ GHSA は、アプリが multipart を使わないなら frontend で弾くよう書いている。
If the application does not use multipart form data, block such requests at the frontend before they reach PHP.
👉 これは経路を 1 つ減らす部分的な対策。しかも告知は、暫定策では導入環境は安全にならず、完全な解決は更新のみ、と明言している。
👉 Nginx 側で multipart を PHP の手前で弾く設定の読み解きは、別記事にまとめた:https://qiita.com/gtakairo/items/1ab335720f8a59205621
CVE-2026-109619 の GHSA は、こう書いている。
There is no complete generic configuration mitigation as an application, extension or server path may publish stale state
Updating to a release containing the complete state reset patch is the only reliable mitigation.
8.1 以前
PHP 8.1 and older no longer receive updates from the PHP project.
👉 8.1 以前には、上流の更新が無い。一部のディストリビューションや LTS ベンダーがバックポートを提供している。配布元に確認する(告知の記述)。
手元のパッケージに修正が入っているか
2026-10-22 に、Ubuntu 24.04 と AlmaLinux 9 のコンテナで確認した。まず Ubuntu。
php -v
PHP 8.3.6 (cli) (built: Oct 19 2026 15:16:58) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.3.6, Copyright (c) Zend Technologies
with Zend OPcache v8.3.6, Copyright (c), by Zend Technologies
dpkg -l php8.3-cli | tail -1
ii php8.3-cli 8.3.6-0ubuntu0.24.04.13 amd64 command-line interpreter for the PHP scripting language
php -v の 8.3.6 は上流の版番号。dpkg -l の 8.3.6-0ubuntu0.24.04.13 は、上流の版(8.3.6)に、配布元のリビジョン(-0ubuntu0.24.04.13)が付いた形。
changelog の先頭はこう。
zcat /usr/share/doc/php8.3-cli/changelog.Debian.gz | head -25
php8.3 (8.3.6-0ubuntu0.24.04.13) noble-security; urgency=medium
* SECURITY UPDATE: Remote RCE via state corruption issue
- debian/patches/CVE-2026-109618_109619.patch: Avoid state corruption via
bailout in Zend/tests/bug40770.phpt, Zend/tests/bug54268.phpt,
Zend/tests/bug76846.phpt,
Zend/tests/debug_backtrace_sensitive_named_variadic.phpt,
Zend/tests/fibers/get-return-after-bailout.phpt, Zend/tests/gh11189.phpt,
Zend/tests/gh11189_1.phpt,
Zend/tests/oom_SplFixedArray___unserialize.phpt,
Zend/tests/oom_SplFixedArray___wakeup.phpt,
Zend/tests/oom_SplFixedArray_copy_ctor.phpt,
Zend/tests/oom_SplFixedArray_fromArray_001.phpt,
Zend/tests/oom_SplFixedArray_fromArray_002.phpt,
Zend/tests/oom_after_main_script.phpt, Zend/tests/oom_array_fill_001.phpt,
Zend/tests/oom_array_fill_002.phpt, Zend/tests/oom_array_fill_003.phpt,
Zend/tests/oom_array_fill_004.phpt, Zend/tests/oom_array_pad_001.phpt,
Zend/tests/oom_array_pad_002.phpt, Zend/tests/oom_array_pad_003.phpt,
Zend/tests/oom_array_pad_004.phpt, Zend/tests/oom_array_pad_005.phpt,
Zend/tests/oom_array_pad_006.phpt, Zend/tests/oom_array_pad_007.phpt,
Zend/tests/oom_array_pad_008.phpt, Zend/tests/oom_call_function.phpt,
Zend/tests/oom_call_function_002.phpt,
Zend/tests/oom_call_function_003.phpt,
Zend/tests/oom_debug_backtrace.phpt,
Zend/tests/oom_debug_backtrace_args.phpt,
👉 changelog の先頭に CVE-2026-109618_109619.patch という名前のパッチと "Remote RCE via state corruption issue" の記載がある。出力から読めるのはここまで。パッチの中身までは見ていない。
次に AlmaLinux 9。
php -v
PHP 8.3.36 (cli) (built: Oct 19 2026 14:00:34) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.3.36, Copyright (c) Zend Technologies
rpm -q php-cli
php-cli-8.3.36-1.module_el9.8.0+330+fdc30075.x86_64
rpm -q --changelog php-cli | grep -E '^- ' | head -15
- rebase to 8.3.36
- rebase to 8.3.33
- rebase to 8.3.32
- drop capstone dependency on i686
- rebase to 8.3.31
- rebase to 8.3.29
- rebase to 8.3.26
- rebase to 8.3.19
- rebase to 8.3.15
- rebase to 8.3.12 RHEL-62189
- enable command history in phpdbg
- backport Argon2 password hashing in OpenSSL ext
- build sockets extension statically
- switch to nikic/php-parser version 5
- openssl: always warn about missing curve_name
👉 この 2 つのコマンドは、別の AlmaLinux 9 コンテナ(dnf module install php:8.3/common で導入。php-cli は同じ 8.3.36-1)で取った。版は 8.3.36。上流の修正版(8.3.36)と同じ番号。2 つ目のコマンドは changelog のエントリ行(- で始まる行)だけを出す。先頭は rebase to 8.3.36。CVE や php2shell の記載は、この 15 行には出ていない。
どちらのディストリビューションについても、「完全に直った」とはこの記事では言わない。読めるのは、Ubuntu は changelog が両 CVE に触れていること、AlmaLinux は版が上流の修正版と同じこと、まで。
版番号だけで判断しない
配布元は、修正をバックポートする。バックポートした修正では、上流の版番号は変わらない。Red Hat はこう書いている。
For most products, our default practice is to backport security fixes.
just looking at the version number of a package will not tell them if they are vulnerable or not.
-
8.3.6-0ubuntu0.24.04.13のように、上流の版が古く見えても、配布元の修正が入っていることがある - 逆に、上流の版番号だけを見て「新しいから問題ない」とも言えない
- 判断は、配布元のセキュリティ情報と changelog で行う
Ubuntu の security tracker:
https://ubuntu.com/security/cves?package=php8.3
👉 URL の案内だけ。どの CVE がどの版で直っているかは、tracker で確認する。
配布元の修正を待てない場合は、自前ビルドの手順を別の記事にまとめた。
https://qiita.com/gtakairo/items/5a655e618d28b5d274ad
例 2:CVE-2024-4577
設定ではなく、PHP のコード自体の欠陥の例をもう 1 つ。CVE-2024-4577 は、Windows で Apache と PHP-CGI を使い、特定の code page を使う設定のときに、Windows の "Best-Fit" 変換でコマンドラインの文字が PHP のオプションと誤解釈され得る問題。Linux には該当しない。修正は 8.1.29 / 8.2.20 / 8.3.8 への更新。
パッケージ更新で設定が壊れない運用
php.ini 本体を直接編集しない。 自分の設定は、読み込み順で後に来るファイルに足す。
| 置き場所 | |
|---|---|
| Debian 系 |
/etc/php/8.3/<sapi>/conf.d/99-local.ini(SAPI ごと)。全 SAPI に効かせるなら、mods-available/ に ini を置いて phpenmod -v 8.3 -s ALL <モジュール名>。最後に読ませるには、ini に ; priority=99 を書く(接頭辞の数字は ; priority= に対応する。第 5 章) |
| RHEL 系 | /etc/php.d/99-local.ini |
👉 99- は、アルファベット順で最後に読まれる(第 4 章)。php.ini の値を上書きできる(php.ini only の expose_php でも効いた。下の確認)。
👉 changeable mode が制限するのは、その後ろの層(pool / .user.ini / ini_set())での変更。
conf.d で上書きできることを確かめる
Ubuntu のコンテナで、expose_php を例にした。
php -i | grep -E '^expose_php'
expose_php => On => On
printf 'expose_php = Off\n' > /etc/php/8.3/cli/conf.d/99-local.ini
出力は無い。
php -i | grep -E '^expose_php'
expose_php => Off => Off
👉 On が Off に変わった。php.ini には触れていない。
配布版の php.ini は production のコピー
diff /usr/lib/php/8.3/php.ini-production /etc/php/8.3/fpm/php.ini; echo exit=$?
exit=0
👉 exit=0 で、差分は無い。Debian 系の /etc/php/8.3/fpm/php.ini は php.ini-production と同じ内容だった(インストール直後のコンテナで)。だから、production を基準に、差分を conf.d に足す運用になる。
更新したとき
Debian 系:php.ini は ucf で管理されている。
grep -n ucf /var/lib/dpkg/info/php8.3-cli.postinst | head -5
19: ucf /usr/lib/php/8.3/php.ini-production.cli $phpini
21: ucf /usr/lib/php/8.3/php.ini-production $phpini
23: ucfr php8.3-cli $phpini
ucfq /etc/php/8.3/cli/php.ini
Configuration file Package Exists Changed
/etc/php/8.3/cli/php.ini php8.3-cli Yes No
- postinst が
ucfを呼んでいる -
ucfqで、/etc/php/8.3/cli/php.iniがphp8.3-cliの管理下にあると分かる
👉 php.ini は ucf の管理下にある(更新時の扱いは ucf に従う)。99-local.ini に分けて php.ini を配布版のままにしておけば、自分の設定が更新で失われることはない。
RHEL 系:設定ファイルは、rpm -qc で見える。
rpm -qc php-common php-fpm | sort
/etc/httpd/conf.d/php.conf
/etc/logrotate.d/php-fpm
/etc/nginx/conf.d/php-fpm.conf
/etc/nginx/default.d/php.conf
/etc/php-fpm.conf
/etc/php-fpm.d/www.conf
/etc/php.d/20-bz2.ini
/etc/php.d/20-calendar.ini
/etc/php.d/20-ctype.ini
/etc/php.d/20-curl.ini
/etc/php.d/20-exif.ini
/etc/php.d/20-fileinfo.ini
/etc/php.d/20-ftp.ini
/etc/php.d/20-gettext.ini
/etc/php.d/20-iconv.ini
/etc/php.d/20-phar.ini
/etc/php.d/20-tokenizer.ini
/etc/php.ini
/etc/systemd/system/httpd.service.d/php-fpm.conf
/etc/systemd/system/nginx.service.d/php-fpm.conf
/etc/php.ini と /etc/php.d/*.ini、/etc/php-fpm.d/www.conf が設定ファイルとして並ぶ。RPM の %config(noreplace) について、マニュアルはこう書いている。
A locally modified %config(noreplace) is preserved as-is on package updates
the new content from the package is saved with .rpmnew suffix for reference.
- 更新時、ローカルで変更した
%config(noreplace)のファイルは、そのまま残る。新しい内容は.rpmnewで置かれる -
.rpmsaveは、変更した%configファイルを削除したときの退避。更新時の話ではない
👉 上の rpm -qc は設定ファイルの一覧で、noreplace の指定までは出ない。/etc/php.ini が %config(noreplace) かどうかは、この出力だけでは確認できていない。
変更後の反映と確認
構文を確認する(Debian 系)。
php-fpm8.3 -t
反映する。
systemctl reload php8.3-fpm
RHEL 系は php-fpm。
systemctl reload php-fpm
👉 php2shell の暫定策(session.upload_progress.enabled=0)は、公式の告知が「全ワーカーを再起動」と書いている(第 8 章)。
mod_php(Apache)は再起動する。
systemctl restart apache2
反映の確認は、php-fpm の設定を絞って見る。
php-fpm8.3 -i | grep -E '^expose_php'
👉 実際にリクエストを処理するプロセスの値は、Web 経由の phpinfo() で確かめる。確認が済んだら phpinfo() のページは消す。
チェックリスト
| 確認項目 | コマンド |
|---|---|
| どの php.ini を読んでいるか |
php --ini(CLI)、php-fpm8.3 -i | grep -E 'Loaded Configuration|Scan this dir'(fpm) |
| conf.d の読み込み順 |
ls -la /etc/php/8.3/fpm/conf.d(RHEL 系は ls -la /etc/php.d) |
| pool の上書き |
grep -vE '^\s*(;|$)' /etc/php/8.3/fpm/pool.d/www.conf(RHEL 系は /etc/php-fpm.d/www.conf) |
| expose_php / display_errors | php-fpm8.3 -i | grep -E '^(expose_php|display_errors)' |
| session cookie | php-fpm8.3 -i | grep -E '^session\.(cookie_httponly|cookie_secure|use_strict_mode|cookie_samesite)' |
| cgi.fix_pathinfo / limit_extensions |
php-fpm8.3 -i | grep cgi.fix_pathinfo、grep limit_extensions /etc/php/8.3/fpm/pool.d/*.conf(行が無ければ既定の .php .phar) |
| 入力上限 | php-fpm8.3 -i | grep -E '^(memory_limit|post_max_size|upload_max_filesize|max_input_vars|session.upload_progress.enabled)' |
| パッケージが修正版か |
dpkg -l php8.3-cli、zcat /usr/share/doc/php8.3-cli/changelog.Debian.gz | head -25(RHEL 系は rpm -q php-cli、rpm -q --changelog php-cli | grep -E '^- ' | head -15) |
| php.ini を直接編集していないか | diff /usr/lib/php/8.3/php.ini-production /etc/php/8.3/fpm/php.ini |
| 構文確認と reload |
php-fpm8.3 -t → systemctl reload php8.3-fpm(php2shell の暫定策は全ワーカーの再起動。第 8 章) |
👉 pool の値は Web 経由の phpinfo() で確かめる。
まとめ
php.ini は SAPI(cli / fpm / apache2)ごとに別。conf.d の断片が php.ini を上書きし、php-fpm では pool 設定がさらに上書きする。編集は php.ini 本体ではなく conf.d(または pool)に置くと、パッケージ更新で壊れない。脆弱性対応の本丸はパッケージ更新で、ini の堅牢化は攻撃面を減らす補助。
- 設定の変更は
99-local.ini(または pool)に足す。php.ini 本体は触らない -
ini_set()や.user.iniで変えられるかは、changeable mode で決まる - 暫定策では導入環境は安全にならない。完全な解決は更新のみ
- 版番号だけで判断せず、配布元の changelog とセキュリティ情報で確かめる
関連記事:
- OPcache と php-fpm:https://qiita.com/gtakairo/items/d44722d345402cd3c316
- Apache / Nginx のセキュリティ設定:https://qiita.com/gtakairo/items/2a7e08c618f802ca3b23
- 脆弱性パッチの自前ビルド(rpmbuild):https://qiita.com/gtakairo/items/5a655e618d28b5d274ad
- Nginx で multipart リクエストを PHP の手前で弾く設定:https://qiita.com/gtakairo/items/1ab335720f8a59205621
参考
- https://www.php.net/manual/en/configuration.file.php
- https://www.php.net/manual/en/configuration.file.per-user.php
- https://www.php.net/manual/en/configuration.changes.modes.php
- https://www.php.net/manual/en/ini.list.php
- https://www.php.net/manual/en/ini.core.php
- https://www.php.net/manual/en/filesystem.configuration.php
- https://www.php.net/manual/en/session.configuration.php
- https://www.php.net/manual/en/install.fpm.configuration.php
- https://www.php.net/archive/2026.php
- https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w
- https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp
- https://nvd.nist.gov/vuln/detail/CVE-2024-4577
- https://access.redhat.com/security/updates/backporting
- https://rpm-software-management.github.io/rpm/man/rpm-spec.5
- https://ubuntu.com/security/cves?package=php8.3