Sicurezza PHP · 13 min read · Jan 22, 2026
Sicurezza di PHP-FPM/Nginx in Ambienti di Hosting Condiviso (Debian/Ubuntu)
Sicurezza di PHP-FPM/Nginx in Ambienti di Hosting Condiviso (Debian/Ubuntu)
Versione 1.0
Autore: Falko Timme
Seguimi su Twitter
Se desideri utilizzare nginx e PHP-FPM per ambienti di hosting condiviso, dovresti riflettere sulla sicurezza. Negli ambienti Apache/PHP, puoi utilizzare suExec e/o suPHP per far eseguire PHP sotto account utente individuali invece di un utente di sistema come www-data. Non esiste nulla di simile per PHP-FPM, ma fortunatamente PHP-FPM ci consente di impostare un “pool” per ciascun sito web che fa eseguire gli script PHP come l’utente/gruppo definito in quel pool. Questo ti offre tutti i vantaggi di suPHP e, oltre a ciò, non hai problemi di trasferimento FTP o SCP perché gli script PHP non devono essere di proprietà di un utente/gruppo specifico per essere eseguiti come l’utente/gruppo definito nel pool.
Non rilascio alcuna garanzia che questo funzionerà per te!
1 Nota Preliminare
Utilizzo un vhost chiamato www.example.com / example.com qui con la radice del documento /var/www/www.example.com/web.
Dovresti avere un’installazione LEMP funzionante, come mostrato in questi tutorial:
- Installazione di Nginx con supporto PHP5 e MySQL su Debian Squeeze
- Installazione di Nginx con PHP5 (e PHP-FPM) e supporto MySQL su Ubuntu 11.04
Una nota per gli utenti di Ubuntu:
Poiché dobbiamo eseguire tutti i passaggi di questo tutorial con privilegi di root, possiamo o anteporre tutte le comandi in questo tutorial con la stringa sudo, oppure diventare root subito digitando
sudo su2 Cosa Abbiamo Finora
Su Debian/Ubuntu, la directory del pool di PHP-FPM è /etc/php5/fpm/pool.d/ - qui verranno creati nuovi pool. Il php.ini utilizzato da PHP-FPM è /etc/php5/fpm/php.ini. C’è già un pool, www.conf - diamo un’occhiata:
vi /etc/php5/fpm/pool.d/www.conf| ; Inizia un nuovo pool chiamato 'www'. ; la variabile $pool può essere utilizzata in qualsiasi direttiva e sarà sostituita dal ; nome del pool ('www' qui) [www] ; Prefisso per pool ; Si applica solo alle seguenti direttive: ; - 'slowlog' ; - 'listen' (unixsocket) ; - 'chroot' ; - 'chdir' ; - 'php_values' ; - 'php_admin_values' ; Quando non impostato, si applica il prefisso globale (o /usr). ; Nota: Questa direttiva può anche essere relativa al prefisso globale. ; Valore Predefinito: nessuno ;prefix = /path/to/pools/$pool ; L'indirizzo su cui accettare le richieste FastCGI. ; Le sintassi valide sono: ; 'ip.add.re.ss:port' - per ascoltare su un socket TCP a un indirizzo specifico su ; una porta specifica; ; 'port' - per ascoltare su un socket TCP a tutti gli indirizzi su una ; porta specifica; ; '/path/to/unix/socket' - per ascoltare su un socket unix. ; Nota: Questo valore è obbligatorio. listen = 127.0.0.1:9000 ; Imposta il backlog di listen(2). Un valore di '-1' significa illimitato. ; Valore Predefinito: 128 (-1 su FreeBSD e OpenBSD) ;listen.backlog = -1 ; Elenco degli indirizzi ipv4 dei client FastCGI ai quali è consentito connettersi. ; Equivalente alla variabile d'ambiente FCGI_WEB_SERVER_ADDRS nella originale ; PHP FCGI (5.2.2+). Ha senso solo con un socket TCP in ascolto. Ogni indirizzo ; deve essere separato da una virgola. Se questo valore è lasciato vuoto, le connessioni saranno ; accettate da qualsiasi indirizzo ip. ; Valore Predefinito: qualsiasi ;listen.allowed_clients = 127.0.0.1 ; Imposta i permessi per il socket unix, se uno viene utilizzato. In Linux, le autorizzazioni di lettura/scrittura ; devono essere impostate per consentire connessioni da un server web. Molti ; sistemi derivati da BSD consentono connessioni indipendentemente dalle autorizzazioni. ; Valori Predefiniti: utente e gruppo sono impostati come l'utente in esecuzione ; modalità è impostata su 0666 ;listen.owner = www-data ;listen.group = www-data ;listen.mode = 0666 ; Utente/gruppo unix dei processi ; Nota: L'utente è obbligatorio. Se il gruppo non è impostato, verrà utilizzato il gruppo dell'utente predefinito ; verrà utilizzato. user = www-data group = www-data ; Scegli come il gestore dei processi controllerà il numero di processi figli. ; Valori Possibili: ; static - un numero fisso (pm.max_children) di processi figli; ; dynamic - il numero di processi figli è impostato dinamicamente in base alle ; seguenti direttive: ; pm.max_children - il numero massimo di figli che possono ; essere vivi allo stesso tempo. ; pm.start_servers - il numero di figli creati all'avvio. ; pm.min_spare_servers - il numero minimo di figli in stato 'inattivo' ; (in attesa di elaborazione). Se il numero ; di processi 'inattivi' è inferiore a questo ; numero, verranno creati alcuni figli. ; pm.max_spare_servers - il numero massimo di figli in stato 'inattivo' ; (in attesa di elaborazione). Se il numero ; di processi 'inattivi' è maggiore di questo ; numero, alcuni figli verranno uccisi. ; Nota: Questo valore è obbligatorio. pm = dynamic ; Il numero di processi figli da creare quando pm è impostato su 'static' e il ; numero massimo di processi figli da creare quando pm è impostato su 'dynamic'. ; Questo valore imposta il limite sul numero di richieste simultanee che saranno ; servite. Equivalente alla direttiva ApacheMaxClients con mpm_prefork. ; Equivalente alla variabile d'ambiente PHP_FCGI_CHILDREN nell'originale PHP ; CGI. ; Nota: Utilizzato quando pm è impostato su 'static' o 'dynamic' ; Nota: Questo valore è obbligatorio. pm.max_children = 50 ; Il numero di processi figli creati all'avvio. ; Nota: Utilizzato solo quando pm è impostato su 'dynamic' ; Valore Predefinito: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 ;pm.start_servers = 20 ; Il numero minimo desiderato di processi server inattivi. ; Nota: Utilizzato solo quando pm è impostato su 'dynamic' ; Nota: Obbligatorio quando pm è impostato su 'dynamic' pm.min_spare_servers = 5 ; Il numero massimo desiderato di processi server inattivi. ; Nota: Utilizzato solo quando pm è impostato su 'dynamic' ; Nota: Obbligatorio quando pm è impostato su 'dynamic' pm.max_spare_servers = 35 ; Il numero di richieste che ciascun processo figlio dovrebbe eseguire prima di essere riavviato. ; Questo può essere utile per aggirare perdite di memoria in librerie di terze parti. Per ; l'elaborazione di richieste infinite specificare '0'. Equivalente a PHP_FCGI_MAX_REQUESTS. ; Valore Predefinito: 0 ;pm.max_requests = 500 ; L'URI per visualizzare la pagina di stato di FPM. Se questo valore non è impostato, nessun URI sarà ; riconosciuto come una pagina di stato. Per impostazione predefinita, la pagina di stato mostra le seguenti ; informazioni: ; connessioni accettate - il numero di richieste accettate dal pool; ; pool - il nome del pool; ; gestore dei processi - statico o dinamico; ; processi inattivi - il numero di processi inattivi; ; processi attivi - il numero di processi attivi; ; processi totali - il numero di processi inattivi + attivi. ; numero massimo di figli raggiunto - numero di volte in cui è stato raggiunto il limite di processo, ; quando pm cerca di avviare più figli (funziona solo per ; pm 'dynamic') ; I valori di 'processi inattivi', 'processi attivi' e 'processi totali' vengono ; aggiornati ogni secondo. Il valore di 'connessioni accettate' viene aggiornato in tempo reale. ; Esempio di output: ; connessioni accettate: 12073 ; pool: www ; gestore dei processi: statico ; processi inattivi: 35 ; processi attivi: 65 ; processi totali: 100 ; numero massimo di figli raggiunto: 1 ; Per impostazione predefinita, l'output della pagina di stato è formattato come text/plain. Passando ; 'html' o 'json' come stringa di query restituirà la corrispondente sintassi di output. ; Esempio: ; http://www.foo.bar/status ; http://www.foo.bar/status?json ; http://www.foo.bar/status?html ; Nota: Il valore deve iniziare con una barra iniziale (/). Il valore può essere ; qualsiasi cosa, ma potrebbe non essere una buona idea utilizzare l'estensione .php o potrebbe ; confliggere con un vero file PHP. ; Valore Predefinito: non impostato ;pm.status_path = /status ; L'URI ping per chiamare la pagina di monitoraggio di FPM. Se questo valore non è impostato, nessun ; URI sarà riconosciuto come una pagina ping. Questo potrebbe essere utilizzato per testare da fuori ; che FPM è vivo e risponde, o per ; - creare un grafico della disponibilità di FPM (rrd o simili); ; - rimuovere un server da un gruppo se non sta rispondendo (bilanciamento del carico); ; - attivare avvisi per il team operativo (24/7). ; Nota: Il valore deve iniziare con una barra iniziale (/). Il valore può essere ; qualsiasi cosa, ma potrebbe non essere una buona idea utilizzare l'estensione .php o potrebbe ; confliggere con un vero file PHP. ; Valore Predefinito: non impostato ;ping.path = /ping ; Questa direttiva può essere utilizzata per personalizzare la risposta di una richiesta ping. La ; risposta è formattata come text/plain con un codice di risposta 200. ; Valore Predefinito: pong ;ping.response = pong ; Il timeout per servire una singola richiesta dopo la quale il processo worker sarà ; ucciso. Questa opzione dovrebbe essere utilizzata quando l'opzione ini 'max_execution_time' ; non ferma l'esecuzione dello script per qualche motivo. Un valore di '0' significa 'spento'. ; Unità disponibili: s(ec) (predefinito), m(inuti), h(ore), o d(ì). ; Valore Predefinito: 0 ;request_terminate_timeout = 0 ; Il timeout per servire una singola richiesta dopo la quale un backtrace PHP sarà ; registrato nel file 'slowlog'. Un valore di '0s' significa 'spento'. ; Unità disponibili: s(ec) (predefinito), m(inuti), h(ore), o d(ì). ; Valore Predefinito: 0 ;request_slowlog_timeout = 0 ; Il file di log per richieste lente ; Valore Predefinito: non impostato ; Nota: slowlog è obbligatorio se request_slowlog_timeout è impostato ;slowlog = log/$pool.log.slow ; Imposta il limite di descrittori di file aperti rlimit. ; Valore Predefinito: valore definito dal sistema ;rlimit_files = 1024 ; Imposta il limite massimo della dimensione core rlimit. ; Valori Possibili: 'illimitato' o un intero maggiore o uguale a 0 ; Valore Predefinito: valore definito dal sistema ;rlimit_core = 0 ; Chroot in questa directory all'avvio. Questo valore deve essere definito come un ; percorso assoluto. Quando questo valore non è impostato, chroot non viene utilizzato. ; Nota: puoi anteporre '$prefix' per chroot al prefisso del pool o a una ; delle sue sottodirectory. Se il prefisso del pool non è impostato, verrà utilizzato il prefisso globale ; invece. ; Nota: chrooting è una grande funzionalità di sicurezza e dovrebbe essere utilizzato ogni volta ; possibile. Tuttavia, tutti i percorsi PHP saranno relativi al chroot ; (error_log, sessions.save_path, ...). ; Valore Predefinito: non impostato ;chroot = ; Chdir in questa directory all'avvio. ; Nota: può essere utilizzato un percorso relativo. ; Valore Predefinito: directory corrente o / quando chroot chdir = / ; Reindirizza stdout e stderr dei worker nel log degli errori principale. Se non impostato, stdout e ; stderr saranno reindirizzati a /dev/null secondo le specifiche FastCGI. ; Nota: in ambienti ad alto carico, questo può causare alcuni ritardi nel tempo di ; processo della pagina (diversi ms). ; Valore Predefinito: no ;catch_workers_output = yes ; Passa variabili di ambiente come LD_LIBRARY_PATH. Tutti i $VARIABLEs vengono presi dall' ; ambiente corrente. ; Valore Predefinito: ambiente pulito ;env[HOSTNAME] = $HOSTNAME ;env[PATH] = /usr/local/bin:/usr/bin:/bin ;env[TMP] = /tmp ;env[TMPDIR] = /tmp ;env[TEMP] = /tmp ; Definizioni php.ini aggiuntive, specifiche per questo pool di worker. Queste impostazioni ; sovrascrivono i valori precedentemente definiti nel php.ini. Le direttive sono le ; stesse della PHP SAPI: ; php_value/php_flag - puoi impostare definizioni ini classiche che possono ; essere sovrascritte dalla chiamata PHP 'ini_set'. ; php_admin_value/php_admin_flag - queste direttive non verranno sovrascritte da ; PHP chiamata 'ini_set' ; Per php_*flag, i valori validi sono on, off, 1, 0, true, false, yes o no. ; Definire 'extension' caricherà l'estensione condivisa corrispondente da ; extension_dir. Definire 'disable_functions' o 'disable_classes' non ; sovrascriverà i valori php.ini precedentemente definiti, ma aggiungerà il nuovo valore ; invece. ; Nota: le opzioni INI del percorso possono essere relative e verranno espanse con il prefisso ; (pool, globale o /usr) ; Valore Predefinito: nulla è definita per impostazione predefinita tranne i valori in php.ini e ; specificati all'avvio con l'argomento -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 |
Come puoi vedere, questo pool sta ascoltando sulla porta 9000 su localhost (127.0.0.1), ed è eseguito come l’utente e il gruppo www-data.
Diamo un’occhiata alla configurazione PHP nel tuo vhost:
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; } [...] } |
La parte importante è la riga fastcgi_pass 127.0.0.1:9000; - questo fa sì che nginx passi le richieste PHP al processo PHP-FPM che ascolta sulla porta 9000 su localhost (127.0.0.1) - come ricordi, questo è il nostro pool definito in /etc/php5/fpm/pool.d/www.conf, il che significa che gli script PHP vengono eseguiti come l’utente e il gruppo www-data.
3 Definire Un Pool Individuale Per Ogni Sito Web
Il mio sito web example.com è di proprietà dell’utente web1 e del gruppo client0, quindi voglio che i miei script PHP vengano eseguiti come quell’utente e gruppo. Pertanto definisco un nuovo pool /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 = / |
Come puoi vedere, faccio ascoltare questo pool sulla porta 9001 invece di 9000, e definisco l’utente come web1 e il gruppo come client0. Puoi definire quanti più pool desideri, ma assicurati di utilizzare una porta non utilizzata per ogni pool (9002, 9003, ecc.).
Ricarica PHP-FPM:
/etc/init.d/php5-fpm reloadOra cambiamo la nostra configurazione vhost per utilizzare il nuovo pool. Tutto ciò che devi cambiare è la porta nella riga 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; } [...] } |
Ricarica nginx successivamente:
/etc/init.d/nginx reloadEcco fatto! Gli script PHP vengono ora eseguiti come l’utente web1 e il gruppo client0.
Puoi rendere PHP ancora più sicuro modificando le impostazioni PHP individualmente per ogni vhost. Dai un’occhiata in fondo a /etc/php5/fpm/pool.d/www.conf, ha alcuni esempi di come raggiungere questo.
Ad esempio, potresti impostare open_basedir o disable_functions nel pool /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 = / 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 |
Ricarica PHP-FPM:
/etc/init.d/php5-fpm reload3.1 Utilizzare Sockets Invece Di Connessioni TCP
Fino ad ora, abbiamo utilizzato connessioni TCP per il nostro pool PHP-FPM (127.0.0.1:9000, 127.0.0.1:9001, ecc.). Questo causa un certo overhead. Fortunatamente possiamo utilizzare socket Unix invece di connessioni TCP per i nostri pool e liberarci di questo overhead. Pertanto, i socket Unix sono più performanti delle connessioni TCP.
Voglio che i socket vengano creati nella directory /var/run/php5-fpm, quindi dobbiamo prima creare quella directory:
mkdir /var/run/php5-fpmPer utilizzare un socket Unix, cambiamo semplicemente la riga listen nella nostra definizione del pool, commentiamo o rimuoviamo la riga listen.allowed_clients (ha senso solo per le connessioni TCP), e aggiungiamo le righe listen.owner (definisce il proprietario del socket), listen.group (definisce il gruppo del socket) e listen.mode (definisce i permessi del socket):
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 = / |
Ricarica PHP-FPM successivamente:
/etc/init.d/php5-fpm reloadDai un’occhiata alla directory /var/run/php5-fpm:
ls -l /var/run/php5-fpmDovresti trovare il socket example.com.sock lì con i permessi 0660, di proprietà dell’utente web1 e del gruppo client0:
root@server1:~# ls -l /var/run/php5-fpm
totale 0
srw-rw---- 1 web1 client0 0 2011-09-21 11:08 example.com.sock
root@server1:~#Infine, dobbiamo cambiare la riga fastcgi_pass nel nostro vhost nginx in 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; } [...] } |
Ricarica nginx successivamente:
/etc/init.d/nginx reloadEcco fatto!
4 Link
- PHP-FPM: http://php-fpm.org/
- nginx: http://nginx.org/
- Wiki di nginx: http://wiki.nginx.org/
- Debian: http://www.debian.org/
- Ubuntu: http://www.ubuntu.com/
Informazioni sull’Autore
Falko Timme è il proprietario di Timme Hosting (hosting web nginx ultra-veloce). È il principale manutentore di HowtoForge (dal 2005) e uno dei core developer di ISPConfig (dal 2000). Ha anche contribuito al libro di O’Reilly “Linux System Administration”.
Ricevi i nuovi post nella tua casella di posta.
Nessuno spam. Disiscriviti in qualsiasi momento.