ラズパイで作る自宅WEBサーバ:nginx編
第4回 PHPインストール

2026.10.21
---------------------------------------------------------------------------------------------
CVE-2026-109618/109619 重大なリモートコード実行の脆弱性対応
PHP 8.5.12、8.4.27、8.3.36、および8.2.35では、認証なしでリモートコード実行を可能にする2つの重大な脆弱性が修正されています。
PHPユーザーは直ちにアップデートすることを強くお勧めします。
例) ubuntuサーバ:php8.3.6 → 8.3.36
$ sudo apt update
$ sudo apt install -y software-properties-common
$ sudo add-apt-repository -y ppa:ondrej/php
$ sudo apt update
$ sudo apt-get -y install php8.3 php8.3-cgi .... ← 必要なモジュールを列記してください
今回の問題は、Apache系WEBサーバで利用されるmod_phpに関連しており、本シリーズで取り上げている nginx では、
mod_php は使用していません。
---------------------------------------------------------------------------------------------
今回は、自宅WEBサーバに、PHPをインストールします。
利用できるパッケージのバージョンを確認します
$ apt-cache show php
Package: php
Architecture: all
Version: 2:8.3+93ubuntu2
インストール可能なコンポーネントを検索します
$ apt-cache search php | grep php8.3 | more
適時必要なPHPモジュールをインストールします
$ sudo apt-get -y install php8.3 php8.3-cgi php8.3-cli php8.3-common php8.3-curl php8.3-gd php8.3-ldap php8.3-mysql php8.3-mbstring php8.3-imap php8.3-xml
インストール済みの拡張モジュールを確認します
$ php -m
●PHP-FPMのインストール
NginxのようなWebサーバーは単独ではPHPコードを実行できません。
そこでPHP-FPM(FastCGI Process Manager)がWebサーバーと連携し、FastCGIというプロトコルを介してPHPスクリプトの実行を橋渡しします。
PHP-FPMの主な役割と仕組み
- プロセス管理(プーリング): あらかじめ待機状態のPHPプロセス(ワーカー)を複数プール(待機)させておきます。
リクエストが来るとこの待機中のプロセスに割り当てるため、プロセスの起動・終了にかかるオーバーヘッドを削減し、高負荷時でもサーバーの負荷を低く抑えます。
- リソース最適化: アクセス数に応じて動的・静的にプロセス数を調整できるため、メモリやCPUを効率的に利用できます。
$ sudo apt install php8.3-fpm
PHP-FPMサービスの確認
$ sudo systemctl --type=service | grep -i fpm
php8.3-fpm.service loaded active running The PHP 8.3 FastCGI Process Manager
$ sudo systemctl status php8.3-fpm
〇 php8.3-fpm.service - The PHP 8.3 FastCGI Process Manager
Loaded: loaded (/usr/lib/systemd/system/php8.3-fpm.service; enabled; preset: enabled)
Active: active (running) since Fri 2026-07-31 06:19:36 JST; 2min 13s ago
●PHP-FPM設定ファイルの編集
PHP-FPMプロセスを実行できるユーザおよびグループが Nginx のシステムユーザ(www-data)になっているか確認します
$ sudo vi /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 # 最大アイドルワーカープロセス数
;request_terminate_timeout = 0 # 単一リクエスト処理のタイムアウト(初期値:無効)
;php_flag[display_errors] = off
;php_admin_value[error_log] = /var/log/php8.3-fpm.log
;php_admin_flag[log_errors] = on
;php_admin_value[memory_limit] = 32M
取り合えず初期設定のまま使用します。設定を変更した場合
$ sudo systemctl restart php8.3-fpm
nginx 設定ファイルに、serverセクションを追加し、locationディレクティブを編集します。
$ sudo vi /etc/nginx/nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /var/log/nginx/error.log;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
}
http {
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
gzip on;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
# サーバー情報の隠匿設定
more_clear_headers 'Server';
more_clear_headers 'X-Powered-By';
more_set_headers 'Server: Apache/2.4.1';
server {
listen 80;
server_name example.com;
root /example/www;
location ~ \.php$ {
try_files $uri = 404;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
client_max_body_size 50M;
client_body_buffer_size 128k;
}
location = /custom_404.html {
internal; # 外部からの直接アクセス拒否
}
location = /custom_50x.html {
internal;
}
}
}
リクエストボディの最大サイズ設定
client_max_body_size 50M;
client_body_buffer_size 128k;
client_max_body_sizeはアップロードなどのリクエストボディの最大サイズを定義し(デフォルト1MB),これを超えると413エラーになります。
client_body_buffer_sizeはメモリ上で処理するバッファサイズ(デフォルト16k〜64k)です。これを超えたデータは一時ファイルに書き込まれます。
$ sudo nginx -t
$ sudo systemctl restart nginx
動作確認のために、PHPの環境変数を表示するスクリプトを作成します
$ vi /example/www/index.php
<?php phpinfo(); ?>
ブラウザから
http://example.com/index.php

PHP-FPM経由で参照される設定ファイルは /etc/php/8.3/fpm/php.ini であることがわかります
●存在しないファイルにアクセスした際のプロキシ抑止
try_files $uri = 404;
try_files ディレクティブは、存在しないファイルへのリクエストがプロキシされないように指定しています。
ブラウザから存在しないURLにアクセスします。
http://example.com/test.php

nginxに用意されているエラーページでは、WEBサーバとそのバージョンが表示されてしまいます。
nginx 設定ファイルでサーバーを偽装しているので、ヘッダー情報では隠蔽されています。
$ curl -I http://example.com/test.php
HTTP/1.1 404 Not Found
Date: Sat, 19 Sep 2026 07:04:13 GMT
Content-Type: text/html
Content-Length: 162
Connection: keep-alive
Server: Apache/2.4.1
カスタムエラーページへのリダイレクトを設定します。
$ sudo vi /etc/nginx/nginx.conf
http {
.....................
# エラー発生時にリダイレクトさせるHTMLのパス
error_page 404 /custom_404.html;
error_page 500 502 503 504 /custom_50x.html;
server {
カスタムエラーページを作成します。
$ sudo vi /example/www/custom_404.html
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
</body>
</html>
再度、アクセスしてみます。
http://example.com/test.php

サーバ内部エラー用にもファイルを作成します。
$ sudo vi /example/www/custom_50x.html
<html>
<head><title>500/501/503/504</title></head>
<body>
<center><h1>Internal Server Error</h1></center>
</body>
</html>
補足:サーバ・エラーの種類
500 Internal Server Error
502 Bad Gateway
503 Service Unavailable
504 Gateway Timeout
●めちゃくちゃなURIでアクセスされた場合の別対処
エラーを通知する代わりに、存在しないファイルへのアクセス要求をすべてトップページの index.php へ飛ばす例です。
server {
......................................
location / {
# 全てのリクエストを /index.php に集約
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ \.php$ {
try_files $uri $uri/ /index.php;
include fastcgi_params;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
client_max_body_size 50M;
client_body_buffer_size 128k;
}
優先順位として正規表現が優先され、存在しないPHPファイルが指定されたときは、index.php に置き換えられます。
htmlファイルなどが指定されると、前方一致により、index.phpに置き換えられます。
デフォルトのフォールバックは、画像専用の location(例: location ~* \.(jpg|png)$)を書かなくても、標準の location / や全体設定が静的ファイルをそのまま処理します。
$is_argsと$argsは、URLのクエリ文字列(GETパラメータ)を転送先やリダイレクト先に引き継ぐために組み合わせて使用する変数です。
$is_argsはクエリがある場合に「?」になり、ない場合は空文字になります。
$argsには実際のパラメータの中身が入ります。
●リクエストURIの書換え
rewriteは、Webサーバーへのアクセス時に、リクエストされたURLを指定した正規表現に基づいて別のURLに動的に書き換える機能です。
ここでは、一例としてブラウザに
http://example.com/123
と打ち込むと、nginx サーバ側で下記のように書き換えてみます。
http://example.com/members/index.php?id=123&sid=abcde
rewriteディレクティブで、正規表現を使ってリクエストURLを別のURLに書き換ます。
$ sudo vi /etc/nginx/nginx.conf
server {
listen 80;
server_name example.com;
root /example/www;
location ~ /([0-9)]+) {
rewrite ^/([0-9)]+)$ /members/index.php?id=$1&sid=abcde;
}
location ~ \.php$ {
try_files $uri = 404;
fastcgi_pass unix:/run/php/php8.3-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
client_max_body_size 50M;
client_body_buffer_size 128k;
}
}
受け側のPHPファイルを作成します。
$ mkdir -p /example/www/members
$ vi /example/www/members/index.php
<?php
echo "Welcome to Member's Page<br />\n";
echo 'SERVER_NAME = '.$_SERVER['SERVER_NAME']."<br />\n";
echo 'SERVER_ADDR = '.$_SERVER['SERVER_ADDR']."<br />\n";
echo 'REMOTE_ADDR = '.$_SERVER['REMOTE_ADDR']."<br />\n";
echo 'REQUEST:id = '.$_REQUEST['id']."<br />\n";
echo 'REQUEST:sid = '.$_REQUEST['sid']."<br />\n";
?>
ブラウザからアクセスします
http://example.com/123

locationディレクティブの優先順位
前方一致より正規表現が優先されます
同じ優先順位の場合、上から順に評価し、マッチしたものが使われます
^~修飾子にマッチした場合は、それ以降のlocationディレクティブは評価されません
| 修飾子 | 説明(上から優先度が高い) |
| = | 完全一致 |
| ~ | 正規表現(大文字小文字を区別) |
| ~* | 正規表現(大文字小文字を区別しない) |
| なし | 前方一致 |
| ^~ | 前方一致 |
●設定ファイル php.ini の編集
設定ファイル php.ini は動作環境によって参照されるファイルが異なります。
コマンドラインで参照される php.ini は下記の通りです。
$ php --ini | grep php.ini
Configuration File (php.ini) Path: /etc/php/8.3/cli
Loaded Configuration File: /etc/php/8.3/cli/php.ini
$ sudo vi /etc/php/8.3/fpm/php.ini
default_charset = "UTF-8" ← デフォルト文字コード
mbstring.language = Japanese ← デフォルトの言語
date.timezone = Asia/Tokyo ← タイムゾーンの設定
expose_php = Off ← HTTPレスポンスによるPHPバージョン非表示
;mbstring.internal_encoding = ← PHP5.6以降で非推奨
;mbstring.http_input = ← PHP5.6以降で非推奨
;mbstring.http_output = ← PHP5.6以降で非推奨
log_errors = On ← エラーログをファイルに残す
error_log = /var/log/php_errors.log ← エラーログの場所
error_reporting = E_ALL & ~E_DEPRECATED & ~E_STRICT
display_errors = Off ← エラー非表示
display_startup_errors = Off ← PHP起動時エラー非表示
session.name = PHPSESSID
session.use_trans_sid = 0
session.use_cookies = 1
Debian系OSでは、拡張モジュールの設定は、php.ini では行いません。(php.ini で extension の指定は必要ありません)
●NGINX Rift(CVE-2026-42945)
NGINX Riftは、NGINXの書き換え(rewrite)モジュールに18年前から存在していた深刻なヒープバッファオーバーフローの脆弱性です。
影響を受けるバージョン:
NGINX Open Source: 0.6.27 から 1.30.0 まで
発生条件:
1.疑問符(?)を含む置換文字列を使用する rewrite ディレクティブがある。
2.その後に、$1や$2といった名前のない正規表現キャプチャ(未命名キャプチャ)を使用する rewrite、if、または set ディレクティブが同じスコープ内で続く。
◎NGINX Rift(CVE-2026-42945)の脆弱性に該当しない例
location ~ /([0-9)]+) {
rewrite ^/([0-9)]+)$ /example.com/index.php?id=$1; # この1行で完結していれば安全
}
NGINX Riftの脆弱性は、「疑問符(?)を含む置換」が行われた後に、同じスコープ内で「別の名前のない正規表現キャプチャ($1など)」が処理されることで、
内部のバッファ計算にズレが生じて発生します。
この例では、1つの rewrite ディレクティブの中で正規表現のキャプチャ($1)とクエリパラメータ(?)の付与を完結させています。
この処理の後に、さらに未命名のキャプチャを呼び出すような別の rewrite、set、if ディレクティブが続いていないため、
脆弱性のトリガーは引き起こされません。
×具体的に危険な記述
本脆弱性のトリガーは、「①疑問符(?)を含む置換」が行われた後に、同じスコープ内で「②名前のないキャプチャ($1、$2など)をさらに処理する」 という組み合わせです。
location ~ ^/api/(.*)$ {
# ① 疑問符(?)を含む置換を行う
rewrite ^/api/(.*)$ /internal?migrated=true;
# ② 直後に、同じスコープ内で名前のないキャプチャ($1)を set に渡している
set $original_endpoint $1;
}
rewrite で ? を含む処理をしたことでNGINX内部の状態フラグが変化し、その状態のまま直後の set で $1 を処理しようとすると、
メモリのサイズ計算が狂ってヒープオーバーフローが発生します。
◎安全なコード(回避策)
location ~ ^/api/(?.*)$ { # (?...) で名付けを行う
rewrite ^/api/.*$ /internal?migrated=true;
set $original_endpoint $endpoint; # 名前付き変数に変えたため安全
}
【参考文献】
・入門 - Nginx + PHP-FPM の仕組み
|