Segurança PHP · 14 min read · Jan 22, 2026
Segurança do PHP-FPM/Nginx em Ambientes de Hospedagem Compartilhada (Debian/Ubuntu)
Segurança do PHP-FPM/Nginx em Ambientes de Hospedagem Compartilhada (Debian/Ubuntu)
Versão 1.0
Autor: Falko Timme
Siga-me no Twitter
Se você deseja usar nginx e PHP-FPM para ambientes de hospedagem compartilhada, deve se preocupar com a segurança. Em ambientes Apache/PHP, você pode usar suExec e/ou suPHP para fazer o PHP executar sob contas de usuários individuais em vez de um usuário do sistema como www-data. Não existe algo assim para PHP-FPM, mas felizmente o PHP-FPM nos permite configurar um “pool” para cada site que faz os scripts PHP serem executados como o usuário/grupo definido nesse pool. Isso lhe dá todos os benefícios do suPHP, e além disso, você não tem problemas de transferência FTP ou SCP porque os scripts PHP não precisam ser de propriedade de um usuário/grupo específico para serem executados como o usuário/grupo definido no pool.
Não dou nenhuma garantia de que isso funcionará para você!
1 Nota Preliminar
Eu uso um vhost chamado www.example.com / example.com aqui com o diretório raiz /var/www/www.example.com/web.
Você deve ter uma instalação LEMP funcionando, como mostrado nesses tutoriais:
- Instalando Nginx com suporte a PHP5 e MySQL no Debian Squeeze
- Instalando Nginx com suporte a PHP5 (e PHP-FPM) e MySQL no Ubuntu 11.04
Uma nota para usuários do Ubuntu:
Como devemos executar todas as etapas deste tutorial com privilégios de root, podemos ou adicionar a string sudo a todos os comandos neste tutorial, ou nos tornarmos root agora digitando
sudo su2 O Que Temos Até Agora
No Debian/Ubuntu, o diretório do pool do PHP-FPM é /etc/php5/fpm/pool.d/ - é aqui que novos pools serão criados. O php.ini usado pelo PHP-FPM é /etc/php5/fpm/php.ini. Já existe um pool, www.conf - vamos dar uma olhada nele:
vi /etc/php5/fpm/pool.d/www.conf| ; Inicie um novo pool chamado 'www'. ; a variável $pool pode ser usada em qualquer diretiva e será substituída pelo ; nome do pool ('www' aqui) [www] ; Prefixo por pool ; Aplica-se apenas nas seguintes diretivas: ; - 'slowlog' ; - 'listen' (unixsocket) ; - 'chroot' ; - 'chdir' ; - 'php_values' ; - 'php_admin_values' ; Quando não definido, o prefixo global (ou /usr) se aplica. ; Nota: Esta diretiva também pode ser relativa ao prefixo global. ; Valor Padrão: nenhum ;prefix = /path/to/pools/$pool ; O endereço no qual aceitar solicitações FastCGI. ; Sintaxes válidas são: ; 'ip.add.re.ss:port' - para escutar em um socket TCP para um endereço específico em ; uma porta específica; ; 'port' - para escutar em um socket TCP para todos os endereços em uma ; porta específica; ; '/path/to/unix/socket' - para escutar em um socket unix. ; Nota: Este valor é obrigatório. listen = 127.0.0.1:9000 ; Defina o backlog do listen(2). Um valor de '-1' significa ilimitado. ; Valor Padrão: 128 (-1 no FreeBSD e OpenBSD) ;listen.backlog = -1 ; Lista de endereços ipv4 de clientes FastCGI que estão autorizados a se conectar. ; Equivalente à variável de ambiente FCGI_WEB_SERVER_ADDRS no original ; PHP FCGI (5.2.2+). Faz sentido apenas com um socket TCP de escuta. Cada endereço ; deve ser separado por uma vírgula. Se este valor for deixado em branco, as conexões serão ; aceitas de qualquer endereço IP. ; Valor Padrão: qualquer ;listen.allowed_clients = 127.0.0.1 ; Defina permissões para o socket unix, se um for usado. No Linux, as permissões de leitura/escrita ; devem ser definidas para permitir conexões de um servidor web. Muitos ; sistemas derivados do BSD permitem conexões independentemente das permissões. ; Valores Padrão: usuário e grupo são definidos como o usuário em execução ; modo é definido como 0666 ;listen.owner = www-data ;listen.group = www-data ;listen.mode = 0666 ; Usuário/grupo unix dos processos ; Nota: O usuário é obrigatório. Se o grupo não for definido, o grupo do usuário padrão ; será usado. user = www-data group = www-data ; Escolha como o gerenciador de processos controlará o número de processos filhos. ; Valores Possíveis: ; static - um número fixo (pm.max_children) de processos filhos; ; dynamic - o número de processos filhos é definido dinamicamente com base nas ; seguintes diretivas: ; pm.max_children - o número máximo de filhos que podem ; estar vivos ao mesmo tempo. ; pm.start_servers - o número de filhos criados na inicialização. ; pm.min_spare_servers - o número mínimo de filhos em estado 'ocioso' ; (aguardando para processar). Se o número ; de processos 'ociosos' for menor que este ; número, então alguns filhos serão criados. ; pm.max_spare_servers - o número máximo de filhos em estado 'ocioso' ; (aguardando para processar). Se o número ; de processos 'ociosos' for maior que este ; número, então alguns filhos serão mortos. ; Nota: Este valor é obrigatório. pm = dynamic ; O número de processos filhos a serem criados quando pm é definido como 'static' e o ; número máximo de processos filhos a serem criados quando pm é definido como 'dynamic'. ; Este valor define o limite no número de solicitações simultâneas que serão ; atendidas. Equivalente à diretiva ApacheMaxClients com mpm_prefork. ; Equivalente à variável de ambiente PHP_FCGI_CHILDREN no original PHP ; CGI. ; Nota: Usado quando pm é definido como 'static' ou 'dynamic' ; Nota: Este valor é obrigatório. pm.max_children = 50 ; O número de processos filhos criados na inicialização. ; Nota: Usado apenas quando pm é definido como 'dynamic' ; Valor Padrão: min_spare_servers + (max_spare_servers - min_spare_servers) / 2 ;pm.start_servers = 20 ; O número mínimo desejado de processos de servidor ociosos. ; Nota: Usado apenas quando pm é definido como 'dynamic' ; Nota: Obrigatório quando pm é definido como 'dynamic' pm.min_spare_servers = 5 ; O número máximo desejado de processos de servidor ociosos. ; Nota: Usado apenas quando pm é definido como 'dynamic' ; Nota: Obrigatório quando pm é definido como 'dynamic' pm.max_spare_servers = 35 ; O número de solicitações que cada processo filho deve executar antes de renascer. ; Isso pode ser útil para contornar vazamentos de memória em bibliotecas de terceiros. Para ; processamento de solicitações infinitas, especifique '0'. Equivalente a PHP_FCGI_MAX_REQUESTS. ; Valor Padrão: 0 ;pm.max_requests = 500 ; A URI para visualizar a página de status do FPM. Se este valor não for definido, nenhuma URI será ; reconhecida como uma página de status. Por padrão, a página de status mostra as seguintes ; informações: ; conexões aceitas - o número de solicitações aceitas pelo pool; ; pool - o nome do pool; ; gerenciador de processos - estático ou dinâmico; ; processos ociosos - o número de processos ociosos; ; processos ativos - o número de processos ativos; ; total de processos - o número de ociosos + processos ativos. ; máximo de filhos alcançados - número de vezes que o limite de processos foi alcançado, ; quando pm tenta iniciar mais filhos (funciona apenas para ; pm 'dinâmico') ; Os valores de 'processos ociosos', 'processos ativos' e 'total de processos' são ; atualizados a cada segundo. O valor de 'conexões aceitas' é atualizado em tempo real. ; Exemplo de saída: ; conexões aceitas: 12073 ; pool: www ; gerenciador de processos: estático ; processos ociosos: 35 ; processos ativos: 65 ; total de processos: 100 ; máximo de filhos alcançados: 1 ; Por padrão, a saída da página de status é formatada como text/plain. Passar 'html' ou 'json' como uma string de consulta retornará a sintaxe de saída correspondente. Exemplo: ; http://www.foo.bar/status ; http://www.foo.bar/status?json ; http://www.foo.bar/status?html ; Nota: O valor deve começar com uma barra inicial (/). O valor pode ser ; qualquer coisa, mas pode não ser uma boa ideia usar a extensão .php ou isso ; pode entrar em conflito com um arquivo PHP real. ; Valor Padrão: não definido ;pm.status_path = /status ; A URI de ping para chamar a página de monitoramento do FPM. Se este valor não for definido, nenhuma ; URI será reconhecida como uma página de ping. Isso pode ser usado para testar de fora ; que o FPM está vivo e respondendo, ou para ; - criar um gráfico de disponibilidade do FPM (rrd ou algo assim); ; - remover um servidor de um grupo se ele não estiver respondendo (balanceamento de carga); ; - acionar alertas para a equipe operacional (24/7). ; Nota: O valor deve começar com uma barra inicial (/). O valor pode ser ; qualquer coisa, mas pode não ser uma boa ideia usar a extensão .php ou isso ; pode entrar em conflito com um arquivo PHP real. ; Valor Padrão: não definido ;ping.path = /ping ; Esta diretiva pode ser usada para personalizar a resposta de uma solicitação de ping. A ; resposta é formatada como text/plain com um código de resposta 200. ; Valor Padrão: pong ;ping.response = pong ; O tempo limite para atender uma única solicitação após o qual o processo trabalhador será ; morto. Esta opção deve ser usada quando a opção ini 'max_execution_time' ; não interrompe a execução do script por algum motivo. Um valor de '0' significa 'desligado'. ; Unidades disponíveis: s(egundos)(padrão), m(inutos), h(oras), ou d(ias) ; Valor Padrão: 0 ;request_terminate_timeout = 0 ; O tempo limite para atender uma única solicitação após o qual um backtrace PHP será ; despejado no arquivo 'slowlog'. Um valor de '0s' significa 'desligado'. ; Unidades disponíveis: s(egundos)(padrão), m(inutos), h(oras), ou d(ias) ; Valor Padrão: 0 ;request_slowlog_timeout = 0 ; O arquivo de log para solicitações lentas ; Valor Padrão: não definido ; Nota: slowlog é obrigatório se request_slowlog_timeout estiver definido ;slowlog = log/$pool.log.slow ; Defina o rlimit do descritor de arquivo aberto. ; Valor Padrão: valor definido pelo sistema ;rlimit_files = 1024 ; Defina o rlimit do tamanho máximo do core. ; Valores Possíveis: 'ilimitado' ou um inteiro maior ou igual a 0 ; Valor Padrão: valor definido pelo sistema ;rlimit_core = 0 ; Chroot para este diretório no início. Este valor deve ser definido como um ; caminho absoluto. Quando este valor não é definido, o chroot não é usado. ; Nota: você pode prefixar com '$prefix' para chroot para o prefixo do pool ou um ; de seus subdiretórios. Se o prefixo do pool não for definido, o prefixo global ; será usado em vez disso. ; Nota: chrooting é um ótimo recurso de segurança e deve ser usado sempre que ; possível. No entanto, todos os caminhos do PHP serão relativos ao chroot ; (error_log, sessions.save_path, ...). ; Valor Padrão: não definido ;chroot = ; Chdir para este diretório no início. ; Nota: um caminho relativo pode ser usado. ; Valor Padrão: diretório atual ou / quando chroot chdir = / ; Redirecionar stdout e stderr do trabalhador para o log de erro principal. Se não definido, stdout e ; stderr serão redirecionados para /dev/null de acordo com as especificações do FastCGI. ; Nota: em ambientes de alta carga, isso pode causar algum atraso no tempo ; de processamento da página (vários ms). ; Valor Padrão: não ;catch_workers_output = yes ; Passe variáveis de ambiente como LD_LIBRARY_PATH. Todas as variáveis $VARIABLE são retiradas do ; ambiente atual. ; Valor Padrão: ambiente limpo ;env[HOSTNAME] = $HOSTNAME ;env[PATH] = /usr/local/bin:/usr/bin:/bin ;env[TMP] = /tmp ;env[TMPDIR] = /tmp ;env[TEMP] = /tmp ; Definições adicionais do php.ini, específicas para este pool de trabalhadores. Essas configurações ; sobrescrevem os valores previamente definidos no php.ini. As diretivas são as ; mesmas que o PHP SAPI: ; php_value/php_flag - você pode definir definições clássicas ini que podem ; ser sobrescritas pela chamada PHP 'ini_set'. ; php_admin_value/php_admin_flag - essas diretivas não serão sobrescritas pela ; chamada PHP 'ini_set' ; Para php_*flag, os valores válidos são on, off, 1, 0, true, false, yes ou no. ; Definir 'extension' carregará a extensão compartilhada correspondente de ; extension_dir. Definir 'disable_functions' ou 'disable_classes' não ; sobrescreverá os valores previamente definidos no php.ini, mas adicionará o novo valor ; em vez disso. ; Nota: opções INI de caminho podem ser relativas e serão expandidas com o prefixo ; (pool, global ou /usr) ; Valor Padrão: nada é definido por padrão, exceto os valores no php.ini e ; especificados na inicialização com o argumento -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 |
Como você vê, este pool está escutando na porta 9000 no localhost (127.0.0.1), e está sendo executado como o usuário e grupo www-data.
Vamos dar uma olhada na configuração do PHP no seu 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; } [...] } |
A parte importante é a linha fastcgi_pass 127.0.0.1:9000; - isso faz com que o nginx passe as solicitações PHP para o processo PHP-FPM escutando na porta 9000 no localhost (127.0.0.1) - como você se lembra, este é nosso pool definido em /etc/php5/fpm/pool.d/www.conf, o que significa que os scripts PHP são executados como o usuário e grupo www-data.
3 Definindo Um Pool Individual Para Cada Site
Meu site example.com é de propriedade do usuário web1 e do grupo client0, então quero que meus scripts PHP sejam executados como esse usuário e grupo. Portanto, defino um novo 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 = / |
Como você vê, faço este pool escutar na porta 9001 em vez de 9000, e defino o usuário como web1 e o grupo como client0. Você pode definir quantos pools quiser, mas certifique-se de usar uma porta não utilizada para cada pool (9002, 9003, etc.).
Recarregue o PHP-FPM:
/etc/init.d/php5-fpm reloadAgora mudamos nossa configuração de vhost para fazer uso do novo pool. Tudo o que você precisa mudar é a porta na linha 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; } [...] } |
Recarregue o nginx em seguida:
/etc/init.d/nginx reloadÉ isso! Os scripts PHP agora estão sendo executados como o usuário web1 e o grupo client0.
Você pode tornar o PHP ainda mais seguro alterando as configurações do PHP individualmente para cada vhost. Dê uma olhada na parte inferior de /etc/php5/fpm/pool.d/www.conf, ele tem alguns exemplos de como alcançar isso.
Por exemplo, você poderia definir open_basedir ou disable_functions no 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 |
Recarregue o PHP-FPM:
/etc/init.d/php5-fpm reload3.1 Usando Sockets em vez de Conexões TCP
Até agora, usamos conexões TCP para nosso pool PHP-FPM (127.0.0.1:9000, 127.0.0.1:9001, etc.). Isso causa alguma sobrecarga. Felizmente, podemos usar sockets Unix em vez de conexões TCP para nossos pools e nos livrar dessa sobrecarga. Portanto, os sockets Unix são mais eficientes do que conexões TCP.
Quero que os sockets sejam criados no diretório /var/run/php5-fpm, portanto, precisamos criar esse diretório primeiro:
mkdir /var/run/php5-fpmPara usar um socket Unix, simplesmente mudamos a linha listen em nossa definição de pool, comentamos ou removemos a linha listen.allowed_clients (faz sentido apenas para conexões TCP) e adicionamos as linhas listen.owner (define o proprietário do socket), listen.group (define o grupo do socket) e listen.mode (define as permissões do 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 = / |
Recarregue o PHP-FPM em seguida:
/etc/init.d/php5-fpm reloadDê uma olhada no diretório /var/run/php5-fpm:
ls -l /var/run/php5-fpmVocê deve encontrar o socket example.com.sock lá com as permissões 0660, de propriedade do usuário web1 e do grupo client0:
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:~#Finalmente, devemos mudar a linha fastcgi_pass em nosso vhost nginx para 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; } [...] } |
Recarregue o nginx em seguida:
/etc/init.d/nginx reloadÉ isso!
4 Links
- PHP-FPM: http://php-fpm.org/
- nginx: http://nginx.org/
- Wiki do nginx: http://wiki.nginx.org/
- Debian: http://www.debian.org/
- Ubuntu: http://www.ubuntu.com/
Sobre o Autor
Falko Timme é o proprietário da Timme Hosting (hospedagem web nginx ultra-rápida). Ele é o principal mantenedor do HowtoForge (desde 2005) e um dos desenvolvedores principais do ISPConfig (desde 2000). Ele também contribuiu para o livro da O’Reilly “Administração de Sistemas Linux”.
Receba novas postagens na sua caixa de entrada
Sem spam. Cancele a assinatura a qualquer momento.