A opção PHP 'cgi.fix_pathinfo' é realmente perigosa com Nginx + PHP-FPM?

51

Houve um monte de falar sobre um problema de segurança em relação à cgi.fix_pathinfoopção PHP usado com Nginx (geralmente PHP-FPM, CGI rápido).

Como resultado, o arquivo de configuração nginx padrão costumava dizer:

# NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini

No entanto, agora, o wiki "oficial" do Nginx afirma que PATH_INFO pode ser tratado corretamente sem desativar a opção PHP acima. E daí?

Questões

  • Você pode explicar claramente o que cgi.fix_pathinfofaz? (o documento oficial diz apenas : "Para obter mais informações sobre PATH_INFO, consulte as especificações CGI")
  • O que o PHP realmente fará com essas PATH_INFOe SCRIPT_FILENAMEvariáveis?
  • Por que e como isso pode ser perigoso com o Nginx? ( exemplos detalhados )
  • O problema ainda existe nas versões recentes desses programas?
  • O Apache é vulnerável?

Estou tentando entender o problema a cada etapa. Por exemplo, não entendo por que usar o soquete php-fpm Unix poderia evitar esse problema.

Totor
fonte
11
Você pode responder a sua própria pergunta por compreender a diferença entre PATH_INFO e PATH_TRANSLATED: blogs.msdn.com/b/david.wang/archive/2005/08/04/...
Giovanni Tirloni

Respostas:

79

TL; DR - a correção (que talvez você nem precise) é MUITO SIMPLES e está no final desta resposta.

Tentarei abordar suas perguntas específicas, mas seu equívoco sobre o que é PATH_INFO torna as perguntas um pouco erradas.

  • A primeira pergunta deve ser "O que é esse negócio de informações do caminho?"

  • Sua próxima pergunta deveria ter sido: "Como o PHP determina o que é PATH_INFOe o que SCRIPT_FILENAMEé?"

    • As versões anteriores do PHP eram ingênuas e tecnicamente nem suportavam PATH_INFO; portanto, o que era suposto ser PATH_INFOcolocado sobre o SCRIPT_FILENAMEqual, sim, é quebrado em muitos casos. Eu não tenho uma versão suficientemente antiga do PHP para testar, mas acredito que ela tenha SCRIPT_FILENAMEvisto o shebang inteiro: "/path/to/script.php/THIS/IS/PATH/INFO" no exemplo acima (prefixado com a docroot como de costume).
    • Com o cgi.fix_pathinfo ativado, o PHP agora encontra corretamente "/ THIS / IS / PATH / INFO" para o exemplo acima e o coloca PATH_INFOe SCRIPT_FILENAMEobtém apenas a parte que aponta para o script que está sendo solicitado (prefixado com a docroot, é claro).
    • Nota: quando o PHP realmente deu suporte PATH_INFO, eles tiveram que adicionar uma configuração para o novo recurso, para que as pessoas que executavam scripts que dependiam do comportamento antigo pudessem executar novas versões do PHP. É por isso que existe até um comutador de configuração. Deveria ter sido incorporado (com o comportamento "perigoso") desde o início.
  • Mas como o PHP sabe qual parte é o script e qual é a informação do caminho? E se o URI for algo como:

    http://example.com/path/to/script.php/THIS/IS/PATH/INFO.php?q=foo

    • Essa pode ser uma pergunta complexa em alguns ambientes. O que acontece no PHP é que ele encontra a primeira parte do caminho do URI que não corresponde a nada na documentação do servidor. Neste exemplo, ele vê que em seu servidor você não possui "/docroot/path/to/script.php/THIS", mas certamente você tem "/docroot/path/to/script.php", então agora o SCRIPT_FILENAMEfoi determinado e PATH_INFOrecebe o resto.
    • Portanto, agora o bom exemplo do perigo detalhado nos documentos Nginx e na resposta de Hrvoje Špoljar (você não pode ser exigente com um exemplo tão claro) fica ainda mais claro: dado o exemplo de Hrvoje (" http: // example. com / foo.jpg / nonexistent.php "), o PHP vê um arquivo na sua docroot" /foo.jpg ", mas não vê nada chamado" /foo.jpg/nonexistent.php "então SCRIPT_FILENAMEobtém" /foo.jpg " (novamente, prefixado com docroot) e PATH_INFOobtém "/nonexistent.php".
  • Por que e como isso pode ser perigoso agora deve ficar claro:

    • O servidor da web realmente não é culpado - é apenas um proxy do URI para o PHP, que inocentemente descobre que "foo.jpg" na verdade contém conteúdo PHP, então ele é executado (agora você foi pwned!). Isso NÃO é específico do Nginx em si.
  • O problema REAL é que você permite que o conteúdo não confiável seja carregado em algum lugar sem sanitização e permite outras solicitações arbitrárias no mesmo local, que o PHP executa felizmente quando pode.
  • O Nginx e o Apache podem ser construídos ou configurados para impedir solicitações usando esse truque, e há muitos exemplos de como fazer isso, inclusive na resposta do usuário2372674 . Este artigo do blog explica bem o problema, mas está faltando a solução certa.

  • No entanto, a melhor solução é garantir que o PHP-FPM esteja configurado corretamente para que ele nunca execute um arquivo, a menos que termine com ".php". Vale a pena notar que as versões recentes do PHP-FPM (~ 5.3.9 +?) Têm isso como padrão, então esse perigo não é mais um problema.

A solução

Se você possui uma versão recente do PHP-FPM (~ 5.3.9 +?), Não precisa fazer nada, pois o comportamento seguro abaixo já é o padrão.

Caso contrário, encontre o www.confarquivo php-fpm (talvez /etc/php-fpm.d/www.confdependa do seu sistema). Certifique-se de ter o seguinte:

security.limit_extensions = .php

Novamente, isso é padrão em muitos lugares hoje em dia.

Observe que isso não impede que um invasor carregue um arquivo ".php" para uma pasta de uploads do WordPress e execute-o usando a mesma técnica. Você ainda precisa ter uma boa segurança para seus aplicativos.

user109322
fonte
5
Boa resposta! Para esclarecer: se, como você diz, o PHP determina o que SCRIPT_FILENAMEé, por que há uma fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;linha no meu nginxconf? Ele substitui o esforço do PHP para descobrir o valor de SCRIPT_FILENAMEsi mesmo?
Totor
Existe uma função para obter o valor de security.limit_extensions? Eu tentei phpinfo(), ini_get(security.limit_extensions)e ini_get_all()sem sucesso.
elbowlobstercowstand
Obrigado, se as versões recentes do PHP-FPM (~ 5.3.9 +?) Têm isso como padrão, por que o php7.1 precisa? Ou este artigo está errado?
Yevgeniy Afanasyev
14

Essencialmente, sem isso, você pode fazer upload de arquivos com código php chamado 'foo.jpg' para o servidor da web; solicite-o como http: //domain.tld/foo.jpg/nonexistent.php e a pilha do servidor da Web digitará erroneamente oh; isto é um PHP; Eu preciso processar isso, ele falhará ao encontrar foo.jpg / nonexistent.php, então ele voltará a foo.jpg e processará foo.jpg como código php. Isso é perigoso, pois abre o sistema para uma invasão muito fácil; qualquer aplicativo da Web que permita o upload de imagens, por exemplo, torna-se uma ferramenta para enviar backdoor.

Em relação ao uso de php-fpm com soquete unix para evitá-lo; IMO não vai resolver o problema.

Hrvoje Špoljar
fonte
Você só repete o que pode ser lido nos links que forneci. Você não explica o mecanismo real. Sua resposta precisa de valor agregado IMHO.
Totor 12/09
6
Isso pode ser verdade, mas seu título tem uma pergunta e a resposta está na minha resposta. Se você quiser explicitamente; sim é perigoso; muito perigoso.
Hrvoje Špoljar
1 / Minha resposta não se limita ao título: tem um corpo. 2 / user109322 provou que você está errado: qualquer valor usado nãocgi.fix_pathinfo é perigoso, porque a configuração padrão é segura (ela executará apenas os arquivos com a extensão). php-fpm.php
Totor
2

No wiki do Nginx como medida de segurança

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

está incluído no bloco de localização. Em outros tutoriais

try_files $uri =404;

é usado, o que deve fazer o mesmo, mas pode causar problemas de acordo com o wiki do Nginx. Com essas opções, cgi.fix_pathinfo=1não deve mais ser um problema. Mais informações podem ser encontradas aqui .

user2372674
fonte