Configuração NginX + PHP-FPM: Por que essa configuração é necessária para configurar corretamente o PATH_INFO? [Debian7.4]

4

Demorou várias horas para descobrir como configurar corretamente NginX , mas ainda acho que posso ter perdido alguma coisa. Principalmente porque esta configuração funcionando perfeitamente gera mais perguntas do que respostas. Talvez isso seja um NginX -bug ou algo introduzido por Debian em Version: 1.2.1-2.2+wheezy2 de Package: nginx-extras , eu simplesmente não sei.

Por favor, note que o meu exemplo já está reduzido ao mínimo (então tome isso como um ponto de partida), mas ele ainda deve conter todas as correções seguras atuais necessárias.

Por favor, note que eu não gosto de heurísticas propensas a erros. Portanto, isso é executado com /etc/php/fpm/pool.d/www.conf configurado como tal, que php-fpm leva o script diretamente de PATH_TRANSLATED em vez de adivinhá-lo:

php_admin_value[cgi.fix_pathinfo]=0
security.limit_extensions = .php
php_admin_flag[expose_php] = off

Aqui está o php -file /home/tino/www/index.php que despeja as variáveis:

<?
header("content-type: text/plain");
$a = array("PATH_TRANSLATED", "SCRIPT_FILENAME", "DOCUMENT_URI", "PATH_INFO", "QUERY_STRING" );
foreach ($a as &$v)
  printf("%-15s %s\n", $v, $_SERVER[$v]);
?>

Trabalhando na configuração NginX

Observe que /home/tino/www é o local do meu script de teste. E você possivelmente deseja preencher todos os outros parâmetros de /etc/nginx/fastcgi_params . Por exemplo, você precisa de

fastcgi_param SCRIPT_NAME $fastcgi_script_name;

para preencher PHP_SELF .

server_tokens off;

server {
        listen          80;
        root            /home/tino/www;

        if ($request_uri ~ " ") { return 444; }

        location ~ [^/]\.php(/|$) {
                limit_except GET HEAD POST { deny all; }

                fastcgi_split_path_info ^((?U).+\.php)(.*)$;

                try_files $fastcgi_script_name =404;

                set             $wtf              $fastcgi_path_info;
                fastcgi_param   PATH_INFO         $wtf;
                fastcgi_param   REQUEST_METHOD    $request_method;
                fastcgi_param   PATH_TRANSLATED   $document_root$fastcgi_script_name;
                fastcgi_param   SCRIPT_FILENAME   $request_filename;
                fastcgi_param   DOCUMENT_URI      $document_uri;

                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
        }
}

Isso me dá a seguinte saída para http://example.com/index.php/a.php?b=b.php

PATH_TRANSLATED /home/tino/www/index.php
SCRIPT_FILENAME /home/tino/www/index.php
DOCUMENT_URI    /index.php
PATH_INFO       /a.php
QUERY_STRING    b=b.php

Isso é exatamente o que eu quero. Note que isso funciona para URIs menos problemáticos também, é claro.

A configuração não funcionando

No entanto, a configuração direta não funciona e estou realmente intrigado, porque:

server_tokens off;

server {
        listen          80;
        root            /home/tino/www;

        if ($request_uri ~ " ") { return 444; }

        location ~ [^/]\.php(/|$) {
                limit_except GET HEAD POST { deny all; }

                fastcgi_split_path_info ^((?U).+\.php)(.*)$;

                try_files $fastcgi_script_name =404;

                fastcgi_param   PATH_INFO         $fastcgi_path_info;
                fastcgi_param   REQUEST_METHOD    $request_method;
                fastcgi_param   PATH_TRANSLATED   $document_root$fastcgi_script_name;
                fastcgi_param   SCRIPT_FILENAME   $request_filename;
                fastcgi_param   DOCUMENT_URI      $document_uri;

                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
        }
}

Isso é o mesmo que acima, apenas a solução $wtf está ausente:

                set             $wtf              $fastcgi_path_info;
                fastcgi_param   PATH_INFO         $wtf;

tornou-se

                fastcgi_param   PATH_INFO         $fastcgi_path_info;

Mas isso não funciona, a saída para http://example.com/index.php/a.php?b=b.php agora é:

PATH_TRANSLATED /home/tino/www/index.php
SCRIPT_FILENAME /home/tino/www/index.php
DOCUMENT_URI    /index.php
PATH_INFO       
QUERY_STRING    b=b.php

Como você pode ver: PATH_INFO desapareceu. Isso não tem nada a ver com php-fpm , é puramente uma coisa NginX !

Configuração insegura funciona novamente

Após a configuração, que abre uma grande falha de segurança devido à verificação do arquivo ausente, funciona novamente:

server_tokens off;

server {
        listen          80;
        root            /home/tino/www;

        if ($request_uri ~ " ") { return 444; }

        location ~ [^/]\.php(/|$) {
                limit_except GET HEAD POST { deny all; }

                fastcgi_split_path_info ^((?U).+\.php)(.*)$;

                fastcgi_param   PATH_INFO         $fastcgi_path_info;
                fastcgi_param   REQUEST_METHOD    $request_method;
                fastcgi_param   PATH_TRANSLATED   $document_root$fastcgi_script_name;
                fastcgi_param   SCRIPT_FILENAME   $request_filename;
                fastcgi_param   DOCUMENT_URI      $document_uri;

                fastcgi_pass unix:/var/run/php5-fpm.sock;
                fastcgi_index index.php;
        }
}

Tem um resultado um pouco diferente no http://example.com/index.php/a.php?b=b.php ( SCRIPT_FILENAME parece errado, DOCUMENT_URI talvez esteja correto de um ponto de vista diferente).

PATH_TRANSLATED /home/tino/www/index.php
SCRIPT_FILENAME /home/tino/www/index.php/a.php
DOCUMENT_URI    /index.php/a.php
PATH_INFO       /a.php
QUERY_STRING    b=b.php

Então, por favor

Aparentemente, try_files quebra como $fastcgi_path_info é analisado depois de definir o regexp com fastcgi_split_path_info .

Então, por favor, alguém pode me dizer se essa solução $wtf é de fato a solução correta ou ainda tenho que me preocupar com efeitos colaterais ruins de try_files ?

Obrigado. (Se eu perdi um pouco de segurança importante, por favor, comente.)

    
por Tino 17.02.2014 / 09:20

2 respostas

4

Eu estava trabalhando ontem na configuração do meu primeiro servidor Nginx com o PHP-FPM e posso ter encontrado o mesmo problema que o seu. Para encurtar a história, eu não fui capaz de configurar o Nginx corretamente e com segurança, com todas as variáveis de servidor do PHP sendo definidas corretamente. Eu finalmente encontrei uma configuração adequada que compartilharei com você. No entanto, seria muito bom ouvir de um especialista da Nginx sobre esse assunto. Sério, nos deparamos com questões realmente estranhas que nunca deveriam acontecer.

Seus problemas de configuração

Então, basicamente, você quer uma instalação Nginx + PHP-FPM segura e funcional. Quero dizer, com o "trabalho", que as variáveis de servidor do PHP estão definidas corretamente e com "seguro", que o Nginx está protegido contra vulnerabilidades conhecidas.

Primeiro , eu consegui reproduzir sua configuração e seus problemas. Por algum motivo, o try_files está quebrando alguma coisa. Eu não posso dizer por que, mas você está certo quando diz isso. A substituição da diretiva try_files pelo código a seguir parece corrigir esse problema:

if (!-f $document_root$fastcgi_script_name) {
    return 404;
}

EDITAR: Como discutido abaixo no comentário, o código acima deve ser evitado . Além disso, a diretiva try_files original está funcionando conforme o esperado.

Segundo , notei que você não incluiu o arquivo fastcgi_params , que é responsável por definir todos os parâmetros necessários (e obrigatórios) do FastCGI que serão usados para definir o PHP variáveis do servidor. Eu não sei porque você não incluiu este arquivo, já que toda a documentação que eu li na web nos diz para usar esse arquivo. Agora que o arquivo está incluído, precisamos modificar alguns parâmetros que não estão definidos corretamente por padrão. No final, precisamos adicionar as seguintes diretrizes:

include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;

Observe como o SCRIPT_FILENAME é diferente do seu. Mais uma vez, eu li muita documentação que nos diz para usar esse valor (vou ligar para aqueles abaixo). Você pode colocar a diretiva fastcgi_param diretamente no arquivo fastcgi_params ou na sua seção location .

Então , nada está funcionando. Se eu usar o mesmo link que o seu, obtenho No input file specified . Examinando os arquivos de log, recebo Unable to open primary script: /var/www/a.php (No such file or directory) . Eu não sei porque está fazendo isso, mas todos os parâmetros do fastcgi parecem ser confusos.

Agora , a parte mais estranha, se eu remover esta linha:

fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;

está funcionando novamente, mas com a confusão de variáveis de servidor do PHP. Não apenas PATH_TRANSLATED não está definido, PHP_SELF e outros não estão definidos corretamente. É aqui que eu luto há muito tempo. Felizmente, encontrei uma solução que parece se encaixar no que pretendemos.

Configuração de trabalho

Se reativarmos cgi.fix_pathinfo na configuração do PHP, tudo funcionará. No começo, pensei que era uma má ideia fazer isso. No entanto, eu li sobre os riscos de segurança com cgi.fix_pathinfo habilitado, apenas para descobrir que já estamos protegidos contra eles. Com security.limit_extensions definido como .php por padrão, estamos protegidos contra cgi.fix_pathinfo problemas. Além disso, a verificação de existência de arquivo (a condição if que adicionamos anteriormente) nos protege ainda mais, evitando o caso em que o FastCGI tenta interpretar outros arquivos como PHP.

A seção location final deve ter esta aparência:

location ~ [^/]\.php(/|$) {
    fastcgi_split_path_info ^((?U).+\.php)(.*)$;

    try_files $fastcgi_script_name =404;

    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;

    fastcgi_pass unix:/var/run/php5-fpm.sock;
    fastcgi_index index.php;
}

Outros desafios

Há uma coisa sobre a qual ninguém fala, mesmo que todos estejam fazendo isso de forma diferente. Estou falando dos regexes usados na diretiva location e fastcgi_split_path_info . Pessoalmente, eu escolho estes porque eles estão fazendo mais sentido para mim:

location ~ ^.+\.php(/|$) { ... }
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;

Eu realmente gostaria de saber as vantagens e desvantagens do outro. Mais uma vez, seria muito bom ouvir um especialista da Nginx sobre esse assunto.

Fontes

por 26.02.2014 / 18:40
1

Não sei por que o seguinte link não apareceu nesta conversa: try_files & $ fastcgi_path_info

The ​try_files directive changes URI of a request to the one matched on the file system, and subsequent attempt to split the URI into $fastcgi_script_name and $fastcgi_path_info results in empty path info - as there is no path info in the URI after try_files.

I don't think this should be considered as a bug, rather than a feature of how try_files work. It makes try_files not very convenient for use with fastcgi_split_path_info, but there are more than one way to workaround it, including the one provided above.

A solução alternativa mencionada acima é a que você mencionou na sua pergunta.

Então, o que você descobriu é uma das armadilhas de nginx e não (a partir de 2016) será corrigido. Sua solução alternativa é perfeitamente válida e, na verdade, citada nos fragmentos de código distribuídos com nginx do Debian.

    
por 30.06.2016 / 21:52

Tags