PHP do Básico ao Avançado – Aula 10 – Um pouco sobre segurança e entrada de dados

Tempo de Leitura: 13 Minutos

Quando falamos em sistemas e internet, na mesma frase uma das primeiras coisas que nos vem em mente é a segurança, e agora que você já aprendeu todos os conceitos da linguagem PHP estará pronto para começar a escrever seus códigos funcionais, ou seja, que realmente fazem alguma coisa, você tem que se preocupar muito com isso. Por este exato motivo, é que resolvi abordar este tema, neste ponto do curso.

PHP é uma linguagem criada, e evoluída especificamente para trabalhar com o ambiente WEB com ela é possível acessar arquivos, executar comandos e abrir conexões de rede no servidor e essas propriedades por si só podem ser consideradas inseguras, e a linguagem PHP é feita com o objetivo específico de rodar aplicações CGI (Common Gateway Interface) e que se configurada de maneira correta é muito mais segura que PERL ou mesmo C neste tipo de ambiente.

Com a escolha correta de opções de configuração em tempo de execução em desenvolvimento e em produção, e uso de práticas corretas de programação, o PHP pode dar a combinação exata de liberdade e segurança que você precisa, existem várias opções de configuração que controlam o comportamento da linguagem num determinado ambiente, e um grande leque de opções que garantem você poder usa-lo para vários propósitos, mas isso também significa que existem combinações de opções e configurações do servidor que resultam em uma instalação insegura.

Podendo ser usado para montar um servidor de aplicações completo, com todo o poder de um usuário shell com privilégios root ou de administrador, ou pode ser usado para inclusão simples de arquivos no lado do servidor com pouco risco em um ambiente bem controlado. E quem faz essas escolhas é justamente você o desenvolvedor, com o auxilio do administrador do servidor, lembre-se, uma equipe de desenvolvimento pode ser tão simples ou tão complexa de acordo com os conhecimentos e objetivos de cada integrante.

Neste primeiro momento, vamos nos atentar somente a boas práticas na programação, e não vamos entrar em detalhes das configurações específicas do ambiente, já que a grande maioria que está aprendendo a desenvolver em PHP vai utilizar um ambiente pré-configurado como um serviço de hospedagem compartilhado onde as configurações e restrições gerais vão estar em um nível mediano (o que é o esperado, já que muita liberdade pode ser inseguro, pois não sabemos quem compartilha a execução, nem restrito a ponto de não permitir nada além de uma informação dinâmica) e cabe ao programador, garantir a segurança do código.

Considerações Gerais

Um sistema completamente seguro é virtualmente impossível de se conseguir, então uma abordagem freqüentemente usada em segurança é um compromisso entre risco e usabilidade de modo a permitir que o trabalho seja executado, sem obstruir ou sobrecarregar o programador ou servidor.  Um sistema é tão bom quanto o elo mais fraco na corrente. Se todas as transações são maciçamente registradas baseado no tempo, localização, tipo de transação,mas o usuário só é verificado baseado em um único cookie, a validade de ligar os usuários ao registro de transação torna-se muito fraca, e a falha pode ser crítica.

A maioria dos ataque explora falhas inseridas justamente pelas entradas de usuários, e grande parte do esforço do programador é para garantir a integridade dessas entradas, e olhar de maneira lógica onde esses dados inesperados possam ser introduzidos e como seu sistema lida com esses dados.

A Internet está cheia de pessoas tentando fazer o próprio nome quebrando o código dos outros, derrubando sites, enviando conteúdo indevido e não importa se você tem um site grande ou pequeno, você é um alvo simplesmente por estar online, tendo um servidor que pode ser conectado. Muitos programas de cracking não discernem por tamanho, eles simplesmente vasculham blocos gigantes de IPs procurando por vítimas.

Servidor Web – Apache (ou outros)

Quando o PHP é usado como módulo do Apache, ele herda as permissões do usuário do Apache (normalmente as do usuário “nobody”). Isso tem vários impactos de segurança e autorização. Por exemplo, se você estiver usando o PHP para acessar um banco de dados, por exemplo um SQLite ou SQLite3, você terá que permitir o controle do arquivo para leitura e escrita, acessível ao usuário “nobody”. Isso significa que um script malicioso pode acessar e modificar o banco de dados, mesmo sem um usuário e senha, ou pior, dependendo de onde está o arquivo, ele pode simplesmente baixa-lo como um arquivo e executa-lo em sua própria máquina. Você pode se proteger contra isso usando autorização do Apache, colocando o arquivo do banco de dados em uma pasta não acessível pelo apache, ou você pode desenvolver seu modelo de acesso prório usando LDAP, arquivos .htaccess, e incluir esse código como parte dos seus scripts PHP.

Normalmente, uma vez que a segurança está estabelecida até esse ponto onde o usuário do PHP (no caso, o usuário do apache, no servidor) tem pouco risco atribuído a ele, você descobre que o PHP também não tem permissão de escrita nos diretórios dos usuários, ou tenha sido proibido de acessar ou alterar bancos de dados com dados reais dos usuários do sitema e que também foi proibido de escrever arquivos, bons ou ruins. Um erro freqüente de segurança feito nesse ponto é permitir ao usuário apache permissões de administrador (root) ou criar arquivos com 777 (todas as permissões para todos os usuário) gerando falhas que podem não comprometer o sistema em php, mais acabam por comprometer outros pontos do servidor e consequentemente do PHP.

Hoje, existem no mercado, ferramentas específicas para administradores de servidores web, garantirem as melhores práticas de segurança, e configurações além, de permitirem de maneira simplificada o setup de novos hosts e contas, bem como uma infinidade de serviços do próprio servidor, se você tem interesse em aprender mais a fundo, recomento buscar informações sobre CPANEL (neste link, tem um tutorial básico explicando sobre ele), WHM (Web Host Manager, veja no link um tutorial do que ele é) dos mesmos desenvolvedores, o PLESK (plesk), o DirectAdmin, Domain Technologie Control, Virtualmin, Hosting Controller, Kloxo, Webmin, ISPconfig entre mais de uma dezena de outros.

File System (arquivos)

O PHP está sujeito à segurança encontrada na maioria dos sistemas de servidor com respeito à permissões de arquivos e diretórios, permitindo controlar quais arquivos no sistema podem ser lidos e por quem. É preciso ter cuidado com quaisquer arquivos que são lidos por todos, e com permissões de leitura e escrita definidas de maneira incorreta, para assegurar que somente o usuário certo terá acesso a um determinado diretório ou um arquivo específico.

PHP foi desenhado para permitir acesso em nível de usuário ao sistema de arquqivos, é possível escrever um script que permitirá ler arquivos do sistema como /etc/passwd, modificar suas conexões de ethernet e outras configurações feitas através de arquivos, enviar trabalhos de impressão e uma infinidade de coisas que podem ser feitas no servidor. Isso tem algumas implicações óbvias, já que você precisa ter certeza que os arquivos que você lê e escreve são apropriados, para uso com determinadas situações, e que ninguém mais tenha acesso a esses dados.

Considere um script onde o usuário pode fazer o upload de imagens, e em outro momento você cria um outro script que lista os arquivos existentes e permite você escolher um para ser apagado:

<?php
// <select name='user_file'>
// No script que cria o formulário, mostramos todos os arquivos, e até verificamos se eles são .jpg para colocar o nome deles nos option
// porem ao receber a variável aqui, esquecemos de validar ou fazemos uma validação erronea

$userfile = $_POST['user_file']; 
unlink("/fotos/$userfile");
echo "arquivo excluído";
?>

Imagina que um hacker envia como valor dessa variável o seguinte ‘../index.php’ ou qualquer outro valor iniciado com ../ ele vai deletar qualquer arquivo mesmo fora do diretório atual, ou em qualquer outra parte do sistema de arquivos, bastando ir testando e lendo as mensagens de erro impressas pela função unlink, como mostrado abaixo, ao se enviar um valor vazio:

Simplesmente ao lêr a mensagem o hacker consegue identificar onde está hospedado o arquivo no diretório físico do servidor e pode enviar valores que excluam não só um arquivo de foto, como um arquivo crucial para o funcionamento do sistema feito em php ou qualquer outro existente no diretório de usuário bastando saber o nome dele. Isso combinado com nomes padrões para diversos arquivos em sistemas unix e windows e isso pode tornar o servidor totalmente inutilizável, ou pelo menos conseguir grandes danos ao sistema.

Existem duas medidas importantes que você deve tomar para prevenir esses problemas. Só dar permissões limitadas ao usuário executando o binário do PHP. Checar todas as variáveis que são enviadas.

<?php
$userfile = basename($_POST['user_file']);
if (file_exists($filepath) && @unlink($filepath)) {
    $logstring = "Excluido $filepath\n";
} else {
    $logstring = "Falha ao excluir\n";
}
echo htmlentities($logstring, ENT_QUOTES);
?>

Note que nesta segunda versão, ainda existem falhas, mais um hacker mesmo mandando um valor inválido não saberá o lugar físico onde a função unlink() está tentando excluir um arquivo, e que neste segundo caso, o path poderia ser inserido de maneira direta, e a função basename() vai escapar e não permitir que o hacker insira um nome fora do diretório que o programador definir.

Dependendo do seu sistema operacional, existe uma variedade enorme de arquivos que você tem que se preocupar, por este motivo, é mais fácil criar uma política onde você proíbe tudo exceto aquilo que for explicitamente permitido. Algumas dicas são:

  1. Melhor não criar arquivos ou pastas com nomes fornecidos pelo usuário. Se você não validar o suficiente, poderá ter problemas. Se isso for necessário, crie uma base de dados onde você associa o nome do usuário com uma string aleatória.
  2. Não permita que qualquer parte da entrada do usuário vá para um comando que será executado. Em vez disso, mantenha um conjunto fixo de comandos com base no que o usuário inseriu, como um case, com opções.
  3. Sempre que você tiver um problema de segurança, o comportamento adequado é negar todos e, em seguida, permitir instâncias específicas. Pela simples razão de que você pode não pensar em todas as restrições possíveis.

Banco de Dados

Atualmente, é quase impossível pensar em um sistema, que não tenha um banco de dados, uma vez que alguma informação sensível ou secreta pode ser guardada em um banco de dados, proteger seus bancos de dados é essencial. Para ler ou gravar qualquer informação nele, você precisa se conectar ao banco de dados, enviar uma consulta (query) legítima, e puxar os dados depois fechar a conexão. Atualmente, a linguagem mais usada para consulta nessa interação é a Structured Query Language (SQL) e a este tipo de manipulação damos o nome de SQL-INJECTION e este tipo de ataque é extremamente simples de ser feito, existem até ferramentas para torna-lo automático como o SQLMAP entre uma dezena de outras.

Para se proteger dela, quantos mais lugares você faz ações para aumentar a proteção do seu banco, menor a probabilidade de um atacante ter sucesso em expor ou abusar de qualquer informação guardada. Usar servidores de banco de dados sempre atualizadas e melhores bibliotecas de acesso aos dados, também é importante; porém, acima de tudo verificar e utilizar de ferramentas para tratar de forma adequada as entradas de usuário.

Este é um dos ataques que mais preocupa um desenvolvedor. Atualmente, muitos frameworks estão trabalhando com camadas de abstração para bancos de dados, e várias abordagens diferentes de lógicas de negócios estão sendo utilizadas, tenha em mente que o que determina se uma estrutura é melhor ou pior é a forma de se utilizar, e neste ponto não devemos ser dogmáticos e avaliar as possíveis soluções.

Bancos de dados, baseados em arquivos, como SQLite, não devem ser usados para guardar dados sensíveis pois possuem regras muito simples para controle de acesso, neste tipo de BD, jamais coloque o arquivo de dados dentro de um diretório acessível na web, ou junto com seus arquivos php. E volto a falar, só use este formato para dados que não sejam cruciais e que não sejam sensíveis em seu conteúdo como senhas, dados de usuário e mesmo dados que possam identificar pessoas ou endereços.

Prefira servidores que possuem uma politica de acesso e controle de acesso em nível do servidor como Oracle, MySQL, MariaDb, MongoDB entre outros. Preferivelmente rode seus scripts PHP com usuários que possuem acesso somente a base de dados que está sendo manipulada, e restrinja ao máximo as permissões do usuário que está conectado ao servidor. Você pode criar usuários de bancos de dados diferentes para cada aspecto da sua aplicação com direitos muito limitados aos objetos do banco de dados. Isso significa que se invasores conseguirem acessar seu bando usando credenciais da sua aplicação, eles só podem afetar o banco tanto quanto sua aplicação ou quanto ao acesso pelo qual aquele ponto que foi invadido possua acesso. Eu o encorajo a não implementar toda a lógica de negócio na aplicação web, ao invés disso, faça parte no esquema do banco, usando view, triggers ou rules, e parte na aplicação, conforme a necessidade de cada parte, novamente, não seja dogmático nem cético.

Se possível, use sempre conexões via SSL para criptografar as transações com o banco de dados, principalmente se o servidor não for na máquina local (mesma do servidor web) desse modo, monitoramento do tráfego e obtenção de informação sobre seu banco de dados serão dificultados por um atacante.

Uma vez que um atacante tenha o acesso direto ao seu banco de dados(desviando do servidor web), os dados armazenados podem ser expostos ou sofrerem uso nocivo, a não ser que a informação seja protegida pelo banco em si. Criptografa os dados é uma boa maneira de diminuir essa ameaça.

Por exemplo, senhas, jamais devem ser armazenadas no banco de dados, para armazena-las utilize sempre um HASH ou funções específicas para tratamento de senhas, que usam técnicas de hash, criptografia, salt, e que podem aumentar muito a segurança dos dados armazenados; além de que existem também funções de criptografia com chaves públicas que são camadas de segurança adicionais, mais como dito lá acima, usar ou não todos os recursos vai aumentar o tempo e o custo de desenvolvimento além de consumir mais recursos que o necessário o que pode ser inviável de acordo com a utilização do sistema.

Como evitar ou mitigar este tipo de ataque SQL

Você pode dizer que o atacante precisa possuir um pouco de informação sobre o esquema de banco de dados na maioria dos exemplos, sim, precisa. Você tem razão, mas você nunca sabe quando e como isso pode ser obtido e, se acontecer, seu banco de dados pode ficar exposto.

Esses ataques se baseam principalmente em explorar falhas no código escrito sem se preocupar com segurança. Nunca confie em nenhum tipo de entrada, especialmente aquela que vem do lado do cliente, mesmo que venha de um combobox, um campo de entrada escondido (hidden) ou um cookie todas essas variáveis podem ser adulteradas por um hacker ou pessoa com um pouco de conhecimento da tecnologia.

Nunca conecte ao banco de dados como um super-usuário ou como o dono do banco de dados. Use sempre usuários personalizados com privilégios bem limitados, se possível, cada parte da sua aplicação deve se conectar ao banco de dados com uma conjunto de credenciais e privilégios diferentes.

Verifique se uma entrada qualquer tem o tipo de dados esperado. O PHP tem um grande número de funções de validação de entrada, que serão vistas nas próximas aulas, além de possuir várias funções de filtros de entrada. Mesmo sendo uma linguagem de tipagem fraca, o desenvolvedor é capaz de validar os tipos e fazer verificações e manipulações desses conteúdos e entradas.

Adicione aspas para cada valor não numérico especificado pelo usuário que será passado para o banco de dados com as funções de caracteres de escape. Sempre que possível use as funções específicas de escape de string do seu banco de dados, ou funções genéricas de escape.

Não imprima qualquer informação específica do banco de dados, especialmente sobre o esquema, prefira tratar as mensagens de erro e deixar mensagens genéricas para o usuário. Você pode usar stored procedures e cursores previamente definidas para abstrair acesso aos dados para que os usuários não acessem tabelas ou views diretamente, mas essa solução pode ter outros impactos, tudo depende da sua aplicação.

Conclusão

Tendo em mente essas preocupações básicas com a segurança dos seus scripts, você já deve conseguir desenvolver qualquer ferramenta ou sistema básico utilizando a linguagem PHP. Na próxima aula, vamos falar sobre o tratamento das mensagens de erro, e como evitar que mensagens de erro possam ser usadas para avaliar possíveis falhas de segurança do seu sistema.

Lembre-se o PHP é uma linguagem segura, da mesma maneira que outras como Java, C, Perl, Python e dependendo do ponto de vista até pode ser considerada mais segura que muitas delas, para garantir a segurança dos seus scripts e dados, utilize sempre versões atualizadas tanto do PHP como dos servidores Web (apache, nginx, iis) como também do sistema operacional e dos servidores de bancos de dados, falhas de segurança são descobertas a cada dia, porém na mesma proporção, soluções para elas vem sendo escritas e implementadas em versões mais novas da linguagem, além da evolução natural dos algoritmos de criptografia, de hash e novas descobertas da tecnologia.