セキュリティ · 4 min read · Jan 22, 2026

PHP-FPM/Nginxの共有ホスティング環境におけるセキュリティ (Debian/Ubuntu)

PHP-FPM/Nginxの共有ホスティング環境におけるセキュリティ (Debian/Ubuntu)

バージョン 1.0
著者: Falko Timme
Twitterでフォローしてください

nginxとPHP-FPMを共有ホスティング環境で使用する場合は、セキュリティについて考慮する必要があります。Apache/PHP環境では、suExecやsuPHPを使用して、PHPをwww-dataのようなシステムユーザーではなく、個々のユーザーアカウントで実行できます。PHP-FPMにはそのようなものはありませんが、幸いなことにPHP-FPMでは、各ウェブサイトのために「プール」を設定でき、PHPスクリプトをそのプールで定義されたユーザー/グループとして実行できます。これにより、suPHPのすべての利点が得られ、さらに、PHPスクリプトはプールで定義されたユーザー/グループとして実行されるため、特定のユーザー/グループに所有される必要がないため、FTPやSCPの転送問題もありません。

これがあなたにとって機能することを保証するものではありません!

1 前提条件

ここでは、www.example.com / example.comというvhostを使用し、ドキュメントルートは/var/www/www.example.com/webです。

これらのチュートリアルに示されているように、動作するLEMPインストールが必要です:

  • Debian SqueezeにPHP5とMySQLサポートを持つNginxをインストールする
  • Ubuntu 11.04にPHP5(およびPHP-FPM)とMySQLサポートを持つNginxをインストールする

Ubuntuユーザーへの注意:

このチュートリアルのすべてのステップをroot権限で実行する必要があるため、このチュートリアルのすべてのコマンドの前に文字列sudoを追加するか、次のようにして今すぐrootになります:

sudo su

2 現在の状況

Debian/Ubuntuでは、PHP-FPMのプールディレクトリは/etc/php5/fpm/pool.d/です。ここで新しいプールが作成されます。PHP-FPMで使用されるphp.iniは/etc/php5/fpm/php.iniです。すでに1つのプール、www.confがあります。これを見てみましょう:

vi /etc/php5/fpm/pool.d/www.conf

| ; 'www'という名前の新しいプールを開始します。 ; 変数$poolは任意のディレクティブで使用でき、 ; プール名(ここでは'www')に置き換えられます。 [www] ; プールごとのプレフィックス ; 次のディレクティブにのみ適用されます: ; - 'slowlog' ; - 'listen'(unixsocket) ; - 'chroot' ; - 'chdir' ; - 'php_values' ; - 'php_admin_values' ; 設定されていない場合、グローバルプレフィックス(または/usr)が代わりに適用されます。 ; 注意: このディレクティブは、グローバルプレフィックスに対しても相対的です。 ; デフォルト値: なし ;prefix = /path/to/pools/$pool ; FastCGIリクエストを受け入れるアドレス。 ; 有効な構文は: ; 'ip.add.re.ss:port' - 特定のアドレスの特定のポートでTCPソケットをリッスンする; ; 'port' - 特定のポートで全アドレスのTCPソケットをリッスンする; ; '/path/to/unix/socket' - unixソケットでリッスンする。 ; 注意: この値は必須です。 listen = 127.0.0.1:9000 ; listen(2)バックログを設定します。'-1'の値は無制限を意味します。 ; デフォルト値: 128(FreeBSDおよびOpenBSDでは-1) ;listen.backlog = -1 ; 接続を許可されているFastCGIクライアントのipv4アドレスのリスト。 ; 元のPHP FCGI(5.2.2+)のFCGI_WEB_SERVER_ADDRS環境変数に相当します。 ; tcpリッスンソケットでのみ意味があります。各アドレスはカンマで区切る必要があります。 ; この値が空白の場合、任意のipアドレスからの接続が受け入れられます。 ; デフォルト値: 任意 ;listen.allowed_clients = 127.0.0.1 ; unixソケットが使用されている場合、unixソケットの権限を設定します。Linuxでは、接続を許可するために読み取り/書き込み権限を設定する必要があります。多くのBSD派生システムは、権限に関係なく接続を許可します。 ; デフォルト値: ユーザーとグループは実行中のユーザーとして設定されます ; モードは0666に設定されます ;listen.owner = www-data ;listen.group = www-data ;listen.mode = 0666 ; プロセスのUnixユーザー/グループ ; 注意: ユーザーは必須です。グループが設定されていない場合、デフォルトユーザーのグループが使用されます。 user = www-data group = www-data ; プロセスマネージャが子プロセスの数を制御する方法を選択します。 ; 可能な値: ; static - 固定数(pm.max_children)の子プロセス; ; dynamic - 子プロセスの数は次のディレクティブに基づいて動的に設定されます: ; pm.max_children - 同時に生存できる子の最大数。 ; pm.start_servers - 起動時に作成される子の数。 ; pm.min_spare_servers - 'アイドル'状態(処理待ち)の子の最小数。この数よりも少ない場合、いくつかの子が作成されます。 ; pm.max_spare_servers - 'アイドル'状態(処理待ち)の子の最大数。この数よりも多い場合、いくつかの子が終了されます。 ; 注意: この値は必須です。 pm = dynamic ; pmが'static'に設定されているときに作成される子プロセスの数。 ; pmが'dynamic'に設定されているときに作成される子プロセスの最大数。 ; この値は、同時リクエストの数の制限を設定します。ApacheMaxClientsディレクティブに相当します。 ; 元のPHP CGIのPHP_FCGI_CHILDREN環境変数に相当します。 ; 注意: pmが'static'または'dynamic'に設定されているときに使用されます ; 注意: この値は必須です。 pm.max_children = 50 ; 起動時に作成される子プロセスの数。 ; 注意: pmが'dynamic'に設定されているときにのみ使用されます ; デフォルト値: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 ;pm.start_servers = 20 ; アイドルサーバープロセスの希望する最小数。 ; 注意: pmが'dynamic'に設定されているときにのみ使用されます ; 注意: pmが'dynamic'に設定されているときに必須 pm.min_spare_servers = 5 ; アイドルサーバープロセスの希望する最大数。 ; 注意: pmが'dynamic'に設定されているときにのみ使用されます ; 注意: pmが'dynamic'に設定されているときに必須 pm.max_spare_servers = 35 ; 各子プロセスが再生成される前に実行するリクエストの数。 ; これは、サードパーティライブラリのメモリリークを回避するのに役立ちます。無限リクエスト処理を指定するには'0'を指定します。PHP_FCGI_MAX_REQUESTSに相当します。 ; デフォルト値: 0 ;pm.max_requests = 500 ; FPMステータスページを表示するためのURI。この値が設定されていない場合、ステータスページとして認識されるURIはありません。デフォルトでは、ステータスページは次の情報を表示します: ; accepted conn - プールによって受け入れられたリクエストの数; ; pool - プールの名前; ; process manager - staticまたはdynamic; ; idle processes - アイドルプロセスの数; ; active processes - アクティブプロセスの数; ; total processes - アイドル + アクティブプロセスの数。 ; max children reached - pmがより多くの子を開始しようとしたときにプロセス制限に達した回数(pm 'dynamic'のときのみ機能します) ; 'idle processes'、'active processes'、および'total processes'の値は毎秒更新されます。'accepted conn'の値はリアルタイムで更新されます。 ; 例: ; accepted conn: 12073 ; pool: www ; process manager: static ; idle processes: 35 ; active processes: 65 ; total processes: 100 ; max children reached: 1 ; デフォルトでは、ステータスページの出力はtext/plainとしてフォーマットされます。クエリ文字列として'html'または'json'を渡すと、対応する出力構文が返されます。例: ; http://www.foo.bar/status ; http://www.foo.bar/status?json ; http://www.foo.bar/status?html ; 注意: 値は先頭スラッシュ(/)で始まる必要があります。値は何でも構いませんが、.php拡張子を使用するのは良いアイデアではないかもしれません。実際のPHPファイルと競合する可能性があります。 ; デフォルト値: 設定されていない ;pm.status_path = /status ; FPMの監視ページを呼び出すためのping URI。この値が設定されていない場合、pingページとして認識されるURIはありません。これは、FPMが生きていて応答しているかどうかを外部からテストするために使用できます。 ; - FPMの可用性のグラフを作成する(rrdなど); ; - 応答しないサーバーをグループから削除する(負荷分散); ; - 運用チームへのアラートをトリガーする(24/7)。 ; 注意: 値は先頭スラッシュ(/)で始まる必要があります。値は何でも構いませんが、.php拡張子を使用するのは良いアイデアではないかもしれません。実際のPHPファイルと競合する可能性があります。 ; デフォルト値: 設定されていない ;ping.path = /ping ; このディレクティブは、pingリクエストの応答をカスタマイズするために使用できます。応答はtext/plainとしてフォーマットされ、200応答コードが返されます。 ; デフォルト値: pong ;ping.response = pong ; 単一リクエストを処理するためのタイムアウト。これを使用すると、'max_execution_time' iniオプションが理由なくスクリプトの実行を停止しない場合に、ワーカープロセスが終了します。'0'の値は'オフ'を意味します。 ; 利用可能な単位: s(秒)(デフォルト)、m(分)、h(時間)、またはd(日) ; デフォルト値: 0 ;request_terminate_timeout = 0 ; 単一リクエストを処理するためのタイムアウト。この後、PHPバックトレースが'slowlog'ファイルにダンプされます。'0s'の値は'オフ'を意味します。 ; 利用可能な単位: s(秒)(デフォルト)、m(分)、h(時間)、またはd(日) ; デフォルト値: 0 ;request_slowlog_timeout = 0 ; 遅いリクエストのログファイル ; デフォルト値: 設定されていない ; 注意: request_slowlog_timeoutが設定されている場合、slowlogは必須です ;slowlog = log/$pool.log.slow ; オープンファイルディスクリプタrlimitを設定します。 ; デフォルト値: システム定義値 ;rlimit_files = 1024 ; 最大コアサイズrlimitを設定します。 ; 可能な値: 'unlimited'または0以上の整数 ; デフォルト値: システム定義値 ;rlimit_core = 0 ; 開始時にこのディレクトリにchrootします。この値は絶対パスとして定義する必要があります。この値が設定されていない場合、chrootは使用されません。 ; 注意: '$prefix'でプレフィックスを付けてプールプレフィックスまたはそのサブディレクトリにchrootできます。プールプレフィックスが設定されていない場合、グローバルプレフィックスが代わりに使用されます。 ; 注意: chrootは優れたセキュリティ機能であり、可能な限り使用すべきです。ただし、すべてのPHPパスはchrootに対して相対的になります(error_log、sessions.save_pathなど)。 ; デフォルト値: 設定されていない ;chroot = ; 開始時にこのディレクトリにchdirします。 ; 注意: 相対パスを使用できます。 ; デフォルト値: 現在のディレクトリまたはchroot時の/ chdir = / ; ワーカーのstdoutとstderrをメインエラーログにリダイレクトします。設定されていない場合、stdoutとstderrはFastCGI仕様に従って/dev/nullにリダイレクトされます。 ; 注意: 高負荷環境では、これによりページ処理時間に遅延が生じる可能性があります(数ミリ秒)。 ; デフォルト値: いいえ ;catch_workers_output = yes ; LD_LIBRARY_PATHのような環境変数を渡します。すべての$VARIABLEは現在の環境から取得されます。 ; デフォルト値: クリーン環境 ;env[HOSTNAME] = $HOSTNAME ;env[PATH] = /usr/local/bin:/usr/bin:/bin ;env[TMP] = /tmp ;env[TMPDIR] = /tmp ;env[TEMP] = /tmp ; このプールのワーカーに特有の追加のphp.ini定義。これらの設定は、以前にphp.iniで定義された値を上書きします。ディレクティブはPHP SAPIと同じです: ; php_value/php_flag - ini_setから上書きできる古典的なini定義を設定できます。 ; php_admin_value/php_admin_flag - これらのディレクティブは、PHP呼び出し'ini_set'によって上書きされません ; php_*flagの有効な値はon、off、1、0、true、false、yesまたはnoです。 ; 'extension'を定義すると、extension_dirから対応する共有拡張がロードされます。 ; 'disable_functions'または'disable_classes'を定義すると、以前に定義されたphp.iniの値は上書きされず、新しい値が追加されます。 ; 注意: パスINIオプションは相対的であり、プレフィックス(プール、グローバルまたは/usr)で展開されます。 ; デフォルト値: php.iniの値と-d引数で起動時に指定された値を除いて、デフォルトでは何も定義されていません。 ;php_admin_value[sendmail_path] = /usr/sbin/sendmail -t -i -f [email protected] ;php_flag[display_errors] = off ;php_admin_value[error_log] = /var/log/fpm-php.www.log ;php_admin_flag[log_errors] = on ;php_admin_value[memory_limit] = 32M |

このプールはlocalhost(127.0.0.1)のポート9000でリッスンしており、ユーザーおよびグループwww-dataとして実行されています。

vhostのPHP設定を見てみましょう:

vi /etc/nginx/sites-available/example.com.vhost

| server { [...] location ~ \.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_script_name; include /etc/nginx/fastcgi_params; } [...] } |

重要な部分は、fastcgi_pass 127.0.0.1:9000;の行です。これにより、nginxはlocalhost(127.0.0.1)のポート9000でリッスンしているPHP-FPMプロセスにPHPリクエストを渡します。これは、/etc/php5/fpm/pool.d/www.confで定義されたプールであり、PHPスクリプトはユーザーおよびグループwww-dataとして実行されます。

3 各ウェブサイトのための個別のプールを定義する

私のexample.comウェブサイトはユーザーweb1とグループclient0によって所有されているため、PHPスクリプトをそのユーザーおよびグループとして実行したいと思います。したがって、新しいプール/etc/php5/fpm/pool.d/example.com.confを定義します:

vi /etc/php5/fpm/pool.d/example.com.conf

| [example.com] listen = 127.0.0.1:9001 listen.allowed_clients = 127.0.0.1 user = web1 group = client0 pm = dynamic pm.max_children = 50 pm.start_servers = 20 pm.min_spare_servers = 5 pm.max_spare_servers = 35 chdir = / |

このプールは9000ではなく9001でリッスンし、ユーザーをweb1、グループをclient0として定義しています。好きなだけプールを定義できますが、各プールに未使用のポート(9002、9003など)を使用することを確認してください。

PHP-FPMを再読み込みします:

/etc/init.d/php5-fpm reload

次に、新しいプールを使用するためにvhost設定を変更します。fastcgi_pass行のポートを変更するだけです:

vi /etc/nginx/sites-available/example.com.vhost

| server { [...] location ~ \.php$ { try_files $uri =404; fastcgi_pass 127.0.0.1:9001; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_script_name; include /etc/nginx/fastcgi_params; } [...] } |

その後、nginxを再読み込みします:

/etc/init.d/nginx reload

これで完了です!PHPスクリプトは現在、ユーザーweb1およびグループclient0として実行されています。

PHPのセキュリティをさらに強化するには、各vhostのPHP設定を個別に変更できます。/etc/php5/fpm/pool.d/www.confの下部を見てみると、これを達成する方法のいくつかの例があります。

たとえば、/etc/php5/fpm/pool.d/example.com.confプールでopen_basedirやdisable_functionsを設定できます。

vi /etc/php5/fpm/pool.d/example.com.conf

| [example.com] listen = 127.0.0.1:9001 listen.allowed_clients = 127.0.0.1 user = web1 group = client0 pm = dynamic pm.max_children = 50 pm.start_servers = 20 pm.min_spare_servers = 5 pm.max_spare_servers = 35 chdir = / php_admin_value[open_basedir] = /var/www/www.example.com:/usr/share/php5:/tmp:/usr/share/phpmyadmin:/etc/phpmyadmin:/var/lib/phpmyadmin php_admin_value[disable_functions] = dl,exec,passthru,shell_exec,system,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,show_source |

PHP-FPMを再読み込みします:

/etc/init.d/php5-fpm reload

3.1 TCP接続の代わりにソケットを使用する

これまで、PHP-FPMプールにTCP接続(127.0.0.1:9000、127.0.0.1:9001など)を使用してきました。これにはいくつかのオーバーヘッドが発生します。幸いなことに、TCP接続の代わりにUnixソケットを使用でき、このオーバーヘッドを取り除くことができます。したがって、UnixソケットはTCP接続よりもパフォーマンスが向上します。

ソケットを/var/run/php5-fpmディレクトリに作成したいので、まずそのディレクトリを作成する必要があります:

mkdir /var/run/php5-fpm

Unixソケットを使用するには、プール定義のlisten行を変更し、listen.allowed_clients行をコメントアウトまたは削除し(TCP接続にのみ意味があります)、listen.owner(ソケットの所有者を定義)、listen.group(ソケットのグループを定義)、listen.mode(ソケットの権限を定義)の行を追加します:

vi /etc/php5/fpm/pool.d/example.com.conf

| [example.com] listen = /var/run/php5-fpm/example.com.sock ;listen.allowed_clients = 127.0.0.1 listen.owner = web1 listen.group = client0 listen.mode = 0660 user = web1 group = client0 pm = dynamic pm.max_children = 50 pm.start_servers = 20 pm.min_spare_servers = 5 pm.max_spare_servers = 35 chdir = / |

その後、PHP-FPMを再読み込みします:

/etc/init.d/php5-fpm reload

/var/run/php5-fpmディレクトリを確認します:

ls -l /var/run/php5-fpm

そこには、権限0660で、ユーザーweb1とグループclient0によって所有されるソケットexample.com.sockが見つかるはずです:

root@server1:~# ls -l /var/run/php5-fpm  
 total 0  
 srw-rw---- 1 web1 client0 0 2011-09-21 11:08 example.com.sock  
 root@server1:~#

最後に、nginx vhostのfastcgi_pass行をfastcgi_pass unix:/var/run/php5-fpm/example.com.sock;に変更する必要があります:

vi /etc/nginx/sites-available/example.com.vhost

| server { [...] location ~ \.php$ { try_files $uri =404; fastcgi_pass unix:/var/run/php5-fpm/example.com.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_param PATH_INFO $fastcgi_script_name; include /etc/nginx/fastcgi_params; } [...] } |

その後、nginxを再読み込みします:

/etc/init.d/nginx reload

これで完了です!

4 リンク

著者について

Falko TimmeはTimme Hosting(超高速nginxウェブホスティング)のオーナーです。彼はHowtoForgeのリードメンテイナー(2005年から)であり、ISPConfigのコア開発者の1人です(2000年から)。彼はまた、O’Reillyの書籍「Linux System Administration」にも貢献しています。

Share: X/Twitter LinkedIn

新しい投稿を受信箱で受け取る

スパムはありません。いつでも購読を解除できます。