Nós concluímos nossa pesquisa em códigos seguros PHP com alguns conselhos sobre diretivas de programação, filtragem de dados fornecidos pelo usuário, e configurações.
Revisão Parte 1o
Diretivas Seguras de Programação O caminho para scripts seguros em PHP é juntar uma combinação cuidadosamente selecionada de configurações e práticas seguras de programação. Baseado nas vulnerabilidades que nos temos estudado até aqui, nós iremos em seguida estabelecer algumas regras que podem ajudar a evitar situações perigosas.
Usando PHP de modo seguro
PHP pode ser configurado de forma que execute scripts em um ambiente restrito para diminuir o dano que pode ser inflingido por programas inseguros. Este modo de operação pode ser chamado de 'safe mode'. A diretiva de configuração safe_mode no php.ini ativa e desativa o safe mode. A diretiva safe_mode_exec_dir especifica um diretório onde os scripts podem ser carregados. PHP não irá executar um script se ele não estiver neste diretório. Alem disto, PHP não irá deixar um script chamar outro programa que não esteja neste diretório. Deste modo, mesmo se existir um buraco na segurança no script que permita invasores executarem arbitrariamente comandos no script, eles irão estar limitados aos que estejam no diretório de executáveis do safe mode.
Para prevenir ocupação de variáveis de ambiente, o safe mode faz uso de outra configuração no php.ini que restringe a habilidade do usuário para modifica-las. O campo safe_mode_allowed_env_vars contem uma lista de prefixos que identificam os nomes de variáveis de ambiente que o usuário tem permitição de modificar. Deste modo, quaisquer variáveis de ambiente cujos nomes começem com algo não listado no safe_mode_allowed_env_vars não pode ser alterado de dentro de um script PHP. A lista padrão consiste no prefixo "PHP_" apenas. Como nos temos visto, algumas das variáveis PHP_ contem informações sensíveis, então esta restrição nem sempre resolve o problema completamente.
Outra configuração de mesma origem é safe_mode_protected_env_vars. A lista fornecida a esta diretiva especifica nomes de variáveis de ambiente que o usuário não pode modificar. As variáves protegidas não podem ser alteradas mesmo se estiverem presente na lista safe_mode_allowed_env_vars. Por padrão, a única variável protegida é $LD_LIBRARY_PATH.
Para aumentar a segurança, o melhor é usar ambas as configuraçõs como complementares, colocando quantas variaveis de risco na diretiva safe_mode_protected_env_vars quanto possivel. Como regra geral, se não é absolutamente necessario para scripts serem capazes de alterar uma variável, proteja-a. De qualquer modo, tente proteger $PATH e amigos. Se isto não é uma solução aceitável, observe todas as variáveis não protegidas com desconfiança e use-as com cuidado.
Então safe mode é bom como conceito, mas não é livre de problemas. Em PHP3, por exemplo, popen() não segue a regra geral de passar tudo atraves do EsacpeShellCmd(). Deste modo, é possivel executar programas externos do diretório especificado em safe_mode_exec_dir:
$fp = popen("ls -l /opt/bin/; /usr/bin/id","r");
echo "$fp
";
while ($line = fgets($fp, 1024)):
printf ("%s
", $line);
endwhile;
pclose($fp);
;
Isto irá imprimir uma listagem do diretório /opt/bin e irá executar /usr/bin/id para mostrar o UID e GID do usuário executando o servidor Web.
Já foi discutido que devido a tais erros de omissão no safe mode, hosts devem não confiar nisto como seguros, mas ao inves disto usar a versão CGI do PHP em combinação com o chroot. Como nos temos visto, isto é dificilmente a melhor alternativa. É, entretando, uma boa ideia não confiar no safe mode exclusivamente, mas usa-lo como reforço para um codigo realmente seguro.
Incluindo arquivos
O servidor Web sabe que um arquivo é um arquivo PHP olhando a extensão .php. Deste modo quando você requer uma página PHP do servidor, ele primeiro interpreta o aquele arquivo e então mostra o resultado. Se o servidor não reconhece a extensão do arquivo, ele irá normalmente apenas mostrar o conteudo do arquivo.
As vezes acontece de o script PHP necessitar incluir outros arquivos como parte dele mesmo. Um monte de programadores tem a tendencia de nomear estes arquivos incluindo com uma extensão .inc. O problema é que se o servidor não for prevenido que aqueles arquivos são atualmente parte de scripts PHP, ele irá mostrar o codigo a quaquer um que peça. Isto dá aos invasores a oportunidade de vasculhar o codigo procurando furos na segurança, todos querem e vem qualquer dado arduamente codificado que pode ser secreto.
Existem vários modos de prevenir isto. Um modo é nomear todos os arquivos incluidos com a extensão .php (ou .php3, ou qualquer uma associada com PHP), assim o servidor irá interpreta-los ao inves de mostra-los. Outra possivel solução é associar arquivos .inc com PHP. Ainda outra solução seria impedir todos os arquivos .inc de serem mostrados. No Apache, esta ultima pode ser conseguida por algo como isto:
Order allow, deny
Deny from all
Isto deve ser uma sessão no arquivo httpd.conf.
Provavelmente a maneira mais segura de fazer isto, entretanto, deve ser não colocar arquivos .inc no diretório definido em DocumentRoot de maneira alguma. Coloque-os em algum outro lugar e mude include_path no php.ini. Este modo apenas seu script PHP irá estar habilitado para usá-los e o servidor Web não irá poder ve-los.
Filtrando dados de usuário
Nós temos visto que dados fornecidos pelo usuário podem causar problemas. Os mais perigosos são metacaracteres que tem significado especial para o shell, ou para a base de dados, ou para qualquer outro programa externo. Filtrar entradas de usuário consiste de tirar qualquer caracter especial de todos os dados que vem de fora. Isto é um modo claramente eficiente de prevenir alguns comportamentos improprios da parte do usuário.
Entradas podem ser filtradas executando uma expressão regular que remova ou escape de um conjunto de metacaracteres que voce fornece. Para simplificar este processo, PHP fornece duas funções que fazem este tipo de coisa. Do manual do PHP:
"EsacpeShellCmd()deixa escapar quaisquer caracteres em uma string que podem ser usadas para levar um comando de shell a executar comandos arbitrários. Esta função deve ser usada para ter certeza de que qualquer dado vindo do usuário é fiscalizada antes deste dado passar para a função exec() ou system(), ou para o operador backtick."
A segunda função confina a string inteira entre aspas simples antes de você passa-la como um argumento para um programa. O shel não associa nenhum significado especial para caracteres dentro de aspas simples, então um pipe por exemplo será passado apenas como parte do argumento, ao invés de repassar a saida como seria seu comportamento normal.
"EscapeShellArg() adiciona aspas simples em torno da string e comenta/deixa escapar qualquer aspas simples existentes permitindo a voce passar uma string diretamente para uma função do shell e te-la tratada como um simples e inofensivo argumento. Esta função deve ser usada para ignorar argumentos individuais para funções do shell vindas da entrada do usuário."
Enquanto aquelas funções fazem o melhor para escapar de simbolos errados conhecidos, existirá sempre alguma chance que percam alguma coisa. Isto é porque diferentes shell em diferentes sistemas tem suas proprias e diferentes ideias de quais caracteres especiais, e uma lista extensiva de todos estes caracteres irá quase certamente ser incompleta ou muito restritiva.
Muitas vezes é melhor construir a sua própria expressão regular para filtrar a entrada do script. Ao inves de filtrar um grande numero de caracteres especiais, entretanto, filtre apenas caracteres que são válidos para aquela entrada em particular. Se o usuário está entrando seu nome por exemplo, apenas permita-o fornecer caracteres alfanuméricos. Para um endereço de e-mail, apenas permita caracteres alfanuméricos, underscore, pontos, traço e o simbolo @. Já que o script tem algumas informações sobre entradas de qualquer forma, use isto para contruir um bom filtro. Isto irá tambem resolve a interação com a base de dados melhor do que o EscapeShellCmd() pode fazer.
Outras Configurações
Muitas outras configurações afetam o comportamento do PHP e pode ser difícil o balanceamente perfeito entre funcionalidade e segurança. Nesta sessão, nós iremos considerar algumas das configurações que são as mais relacionadas com a segurança de scripts PHP e iremos discutir seu significado.
Quase todas estas configurações são encontradas no arquivo de configuração do PHP, php.ini (ou php3.ini para PHP3). Este arquivo é lido e processado toda vez que o interpretador PHP é chamado. Dependendo de como PHP é instalado, isto pode ser apenas uma vez (se php é instalado como módulo do servidor Web), ou uma vez para toda execução de script (se PHP é instalado como aplicação CGI). Os valores de algumas configurações especificadas no arquivo de configuração pode ser acessado e até modificado em tempo de execução a partir do script usando ini_get() e ini_set(), respectivamente. Isto significa que configurações de segurança podem ser ajustadas em um script base, mas tambem significa que scripts cuidadosamente projetados tem potencial de evitar suas restrições de segurança. Deste modo, funções que acessam as opções de configuração não devem ser alimentadas por entradas de usuario, mesmo que tenha sido filtrado.
Em adição, para instalações do PHP como modulo do Apache, a configuração pode tambem ser alterada dos arquivos locais httpd.conf e .htaccess para cada diretório onde os scripts estão. Isto pode ser usado para dar grupos de scripts com diferentes restrições e privilégios, mas novamnte algumas destas configurações pode ser mudada de dentro do script.
Duas opções de configuração diretamente controlam o safe mode do PHP, que foi discutido previamente - safe_mode, que pode ser ativado ou desativado, e a diretiva safe_mode_exec_dir, o qual especifica um diretório de onde scripts PHP são permitidos executar programas externos quando o safe mode estiver ativado. Outra configuração relacionada com o safe mode é o doc_root - PHP não irá entregar arquivos que estejam fora deste diretório quando estiver em safe mode.
A diretiva open_basedir especifica a raiz da arvore de diretórios fora dos quais scripts não tem permissão de abrir arquivos. Isto é, um script não pode usar fopen(), por exemplo, em um arquivo que não esteja na mesma arvore de diretório. Por padrão, open_basedir é vazio e PHP permite arquivos serem abertos de qualquer lugar (determina que os scripts tenham as permissões de acesso apropriadas).
Quando executando como um módulo do Apache, a diretiva enable_dl
intrui o PHP se deve ou não habilitar a carga dinâmica de modulos PHP
com dl().
A possibilidade de carregar arquivos dinâmicamente é habilitada por
padrão, mas quando em safe mode, PHP não irá permitir o uso de dl() de
maneira alguma, porque é possivel evitar open_basedir e outras
restrições com modulos dinâmicamente carregados.
Outra diretiva restritiva é disable_functions. Esta lista de nomes, separados por virgula, de funções que PHP irá apenas ignorar. Colocar dl() nesta lista é outro modo de proibir o carregamento dinâmico. Provavelmente é bom colocar o phpinfo() aqui, porque ele fornece muitas informações a respeito do script e do host. Para ter uma segurança reforçada, você pode desabilitar mail(), system() e amigos, até mesmo include(). É claro que isto tambem limita a funcionalidade do script severamente. Geralmente, é uma boa idéia disabilitar funções que tem o potencial de danificar o sistema e que os scripts possam fazer sem estas.
Por padrão, PHP faz todas as variáveis de ambiente e servidor, todos os cookies e variáveis GET e POST globalmente acessíveis pelo nome. Isto ajuda programadores novatos no PHP - eles não tem compreensão de como restaurar estes dados externos. Mas é uma prática perigosa em que configurações importantes podem acidentalmente serem mudadas muito tranparente e facilmente. Pior ainda, não existe muito para parar um invasor de submeter um campo de formulário com o mesmo nome que a variável contendo algum nome de arquivo, por exemplo. A diretiva de configuração register_globals controla este aspecto do comportamento do PHP, e parece apropriado desativar. O padrão desta diretiva é estar ativado.
Sobre o autor Jordan Dimov é um consultor da Cigital Inc em Dulles, VA, e membro do grupo de segurança de software da Cigital.
Tradução e adaptação: Sulamita Garcia