PHP do Básico ao Avançado – Aula 11 – Tratando erros e evitando exposição de dados sensíveis
Como visto na aula anterior, algumas vezes, mensagens de erro padrão podem expor dados sensíveis como endereço físico do sistema de arquivos onde um arquivo está sendo manipulado, incluído (require ou include) ou mesmo excluído. Um nome de usuário/senha podem ser expostos ou printados na tela caso um arquivo seja executado ou lido de maneira indevida, uma falha no tratamento de dados pode permitir a injeção de SQL em nosso sistema, ou mesmo um manipulador de sessão ou outro dado de uma API interna pode ser exposto, para mitigar esse tipo de falha, devemos saber onde e como elas podem ser exploradas.
Mantenha em mente essa regra simples: defesa em profundidade. Quanto mais lugares você tiver ações para aumentar a proteção do seu banco de dados ou sistema de arquivos, menor a probabilidade de um atacante ter sucesso em abusar de qualquer informação armazenada.
Quando salvamos um dado, seja em um banco de dados ou um arquivo, podemos fazer uso de criptografia, e hash para garantir que a informação não seja legível “A OLHO NÚ” ou seja, sem técnicas mais avançadas, se uma informação é salva diretamente em um arquivo, basta ao atacante acesso a este arquivo e ele conseguirá ler todo o conteúdo como se estivesse lendo uma página web ou um livro; já quando estamos usando criptografia, o que ele lê são os caracteres criptografados, que precisam passar por algoritmo para serem descriptografados e poderem ser lidos novamente; já objetos que devem sempre ser escondidos como senhas, é possível o uso de hash e validar um determinado conteúdo ou envio de um arquivo também pode ser avaliado com hash.
Quando apresentamos erros ao usuário, estes podem apresentar uma mensagem útil como identificar um erro de grafia ou uma mensagem de falha em algum componente ou mesmo em outro servidor ou serviço que estamos usando como uma conexão ao banco de dados, acesso a um outro protocolo ou API como também pode fornecer mensagens que possam ajudar um hacker a ter sucesso em um ataque contra o servidor onde a aplicação está sendo executada.
Os erros do PHP que são normalmente retornados podem ser úteis para um desenvolvedor que está tentando depurar um script, indicando coisas como a função ou arquivo que falhou, o arquivo e a linha onde a falha ocorreu, e mesmo qual o código causador da falha. Porém, em um servidor de produção, pode expor variáveis ocultas, sintaxe incorreta, ou outra informações perigosas. Especialmente perigoso é rodar código de fontes conhecidas com tratadores de depuração integrados, ou usar técnicas de depuração comuns.
Uma mensagem de erro pode indicar se o sistema está executando um engine de banco de dados específico, ou dar dicas de como uma página foi programada ou desenhada, mostrando mensagens que exponham o sistema de arquivos. Isso permite uma investigação profunda sobre portas abertas, ou procurar por bugs específicos ou fraquezas de uma página, como manipuladores de sessão, ou valores inválidos para variáveis que estão escapando da validação de erros. Enviando diferentes pedaços de dados ruins, um atacante pode determinar a ordem de autenticação em um script, (a partir da linha do erro) ou sondar por exploits que podem ser aproveitados em diferentes partes do script como reutilização e sobrecarga de funções.
Um erro geral do PHP ou do sistema de arquivos indicam quais permissões o servidor web tem, assim como a estrutura e organização dos arquivos no servidor web. Existem três soluções principais para esse tipo de problema. A primeira é verificar exaustivamente todas as funções. A segunda é desabilitar completamente os relatórios de erros no código de produção (isso é uma das melhores opções, lembre-se que você ainda pode fazer o LOG dos erros em um arquivo inacessível pelo servidor Web, e que só pode ser visto por exemplo pelo FTP. A terceira é usar as funções personalizadas de tratamento de erro para criar seu próprio tratamento de erro. Dependendo da política de segurança, você pode perceber que todas são aplicáveis à sua situação.
Uma boa prática é possuir valores diferentes de configurações no php.ini e em outros arquivos de configuração como .htaccess ou user.ini ou qualquer outro definido na sua configuração, para o servidor de desenvolvimento, e de homologação e produção, ou seja, entanto está rodando no desenvolvimento, mensagens de erro podem ser apresentadas na tela e logadas em um arquivo, em servidores de homologação e teste, você pode não apresentar todas as mensagens e somente as mais relevantes, e ao invés de mostrar na tela, só salvar em um arquivo de log, evitando a exposição de dados. Já nos servidores de produção, devemos deixar somente erros que possibilitem o rastreio de um atacante, e jamais expor dados na tela, se possível, tratando os erros para apresentar mensagens personalizadas, que indiquem a falha e que em hipótese alguma exponham qualquer erro seja ele do sistema de arquivos ou de conexões ou mesmo da execução do script ou dados do servidor.
Boas Práticas de Programação, Sempre São BOAS PRÁTICAS DE PROGRAMAÇÃO
Sei que cometi um erro de redundância gramatical, mais foi proposital para frisar BOAS PRÁTICAS DE PROGRAMAÇÃO que é a mensagem principal dessa parte do texto. Mesmo que o PHP permita que variáveis tenham seu tipo alterado, ou não sejam previamente inicializadas ele também não proíbe essas práticas e adota-las é a diferença entre um bom e um mau programador.
Vejo muito em grupos de discussão na internet sobre programação, comentários onde linguagens (SIM LINGUAGENS) como JavaScript e PHP são referenciadas com uma certa descriminação, por parte de outros programadores justamente por essa tipagem mais fraca ou pela não exigência de algumas formalidades de outras linguagens, porém, ela permitir isso não significa que é a pratica indicada, e um bom programador vai tomar certos cuidados que vão garantir o bom uso da linguagem, a tipagem correta e vai ser validada e avaliada com a mesma precisão e segurança de uma linguagem como JAVA ou C/C++
Um código inseguro, e com falhas, pode ser escrito em PHP, e em JAVA da mesma maneira, cada linguagem tem um propósito diferente, por exemplo, usar C para escrever um aplicativo web é possível sim, porém, provavelmente ele vai possuir mais falhas de segurança do que o mesmo aplicativo escrito em PHP o que vai determinar é a qualidade dos programadores e da equipe envolvida, e o PHP já possui um grupo de desenvolvedores que estão escrevendo o CORE da linguagem de maneira a torna-la o mais segura possível quanto a diversas falhas já localizadas, enquanto desenvolver algo a partir do nada, todos os processos terão que ser validados e checados o que com certeza vai aumentar consideravelmente o custo e o tempo.
No PHP é possível tomar medidas preventivas para avisar quando alguém está tentando criação de variáveis falsas. Se você sabe de antemão de onde uma variável deve vir, você pode verificar se os dados enviados estão chegando de maneira incorreta. Embora isso não seja garantia de que os dados não são forjados, torna necessário ao atacante adivinhar o tipo certo de falsificação.
Em resumo, o PHP se adapta ao nível de segurança exigido pela sua aplicação, veja, se a ideia é só criar uma página de um contador de visitas, você não precisa de criptografia e muita segurança, então, você pode aproveitar dessas facilidades da linguagem, agora se o projeto é para algo maior, como um aplicativo baseado em Web você não só pode, como deve seguir algumas regras que vão aumentar a segurança e a confiabilidade de sua aplicação.
A maior fraqueza na maioria dos programas PHP não é inerente a linguagem em si, mas meramente um problema de código escrito desconsiderando a segurança. Por essa razão, você sempre deve investir um pouco de tempo considerando as implicações de um certo pedaço de código, para ter certeza que o dano possível se uma variável não esperada for submetida ao mesmo.
Entradas de Usuário
Você sempre deve examinar cuidadosamente o código para assegurar que quaisquer variáveis sendo enviadas do lado do usuário, ou seja, do navegador web estão sendo checadas de maneira correta, e faze a si mesmo as seguintes perguntas:
- Seu script só afetará os arquivos desejados? Ou é possível escapar para outros diretórios do sistema de arquivos do servidor com caracteres e codificações em formato de URL?
- Dados incomuns ou indesejados podem ser utilizados? Valores textuais ou NULOS para variáveis?
- Esse script pode ser usado de maneiras não intencionadas? Ou Executar funções adversas se o código for executado por um terceiro? Funções que mandem e-mail e que possam ser chamadas com outros arquimentos?
- Ele pode ser usado em conjunto com outros scripts de maneira negativa? Para disparar emails ou ler e excluir arquivos que possam ser passados para ele de maneira inadvertida?
- As transações serão registradas adequadamente? E podem ser desfeitas caso exista uma falha de segurança?
Respondendo essas perguntas adequadamente enquanto estiver desenvolvendo sua aplicação, você previne a reescrita indesejada quando você precisar aumentar a segurança.
Uma boa prática é iniciar com essa linha de raciocínio, você não garante a segurança do seu sistema, mas pode ajudar a aumentá-la de maneira gradual se necessário. Trabalhar em tempo de desenvolvimento, com o modo error_reporting(E_ALL) também pode te ajudar avisando sobre variáveis sendo usadas antes de serem checadas ou inicializadas, e você pode prevenir que dados incomuns sejam operados ou inseridos no seu banco ou arquivos.
Algumas dicas básicas
COOKIES
Alterar os nomes padrão dos cookies de sessão do PHP ajuda a prevenir roubos de sessão, muito malwares e crackers analisam os cookies do lado do cliente, procurando por cookies com os nomes padrões e enviam os dados para um atacante para ser feito algo que é chamado de roubo de sessão, onde um usuário autenticado corretamente tem seu cookie de sessão roubado e outro usuário (hacker) passa a utilizar a sessão ativa.
Para isso, uma função básica é criar nomes de sessão diferentes, baseados em dados do usuário, como em:
session_name(md5('app'.$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT']));
Outra dica é habilitar a diretiva session.cookie_httponly no seu php.ini para evitar que o Javascript acesse e manipule seus dados, evitando ataques de XSS (Cross Script).
SESSIONS
Sessões de usuário são um assunto a parte, e teremos aula especificamente sobre isso mais a frente, porém é importante deixar algumas dicas nesse momento. A primeira delas é além de ter criado um nome de cookie de sessão específico, fazer validações para saber se essa sessão foi iniciada por um determinado usuário.
// no login você criou a session e definiu o name como no exemplo acima // em todas as páginas você cria o token baseado em dados do usuário, e valida, se não for, destroi a sessão $TkUser=md5('app'.$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT']); if($_SESSION['name']!=$TkUser){ die('Sessao alterada, faça novo login'); } // continua com o script validado
Uma outra dica, é sempre expire a sessão com um determinado tempo, para isso o PHP tem uma função que deve ser chamada ANTES do inicio da sessão:
session_cache_expire(10); // define o tempo para 10 minutos session_start();
FORMULÁRIOS
Sempre valide a origem dos dados, e mesmo em um formulário de cadastro, você pode gerar um TOKEN e compara-lo com o dado recebido, garantindo a origem dos dados (por um determinado IP por exemplo). Ou seja, mesmo um usuário não autenticado, ainda assim terá sua sessão e origem rastreados, permitindo o log de ações de cada usuário.
Essa técnica auxilia na identificação de um possível ponto de vazamento de dados, ou mesmo onde as falhas de segurança podem ser facilmente rastreadas.
Para isso, na criação do formulário, você insere o token em um campo hidden e depois no recebimento dos dados, você confere esses dados, veja um exemplo:
<?php $tokenForm=md5('formName'.$_SERVER['REMOTE_ADDR'].$_SERVER['HTTP_USER_AGENT'].time()); $_SESSION['formtoken']=$tokenForm; ?> <form action=salva1.php method=post> <input type='hidden' name='tokenform' value='<?php echo $tokenForm; ?>'> <input type='text' name='myemail'> </form>
Depois no arquivo que recebe os dados, você coloca:
<?php //antes deste ponto, você já deve ter iniciado a sessão, verificado a sessão e vai iniciar a recepção dos dados if($_POST['tokenform']!=$_SESSION['formtoken']){ die('Form não autorizado, ou inválido'); } // continue aqui com a filtragem dos dados do form de input ?>
Quando você receber os dados, SEMPRE, valide os dados e faça os devidos filtros conforme o tipo de dado, nunca confie em um dado vindo de um GET, POST, COOKIE ou mesmo da variável $_SERVER pois um atacante pode manipular essas informações, se fazendo passar e tentando fazer a injeção de código no seu script, ou fazendo a injeção SQL.
O PHP possui funções específicas para filtagem de dados, de acordo com o tipo de dado, e com a necessidade, mais a frente vamos falar especificamente dessas funções – htmlspecialchars(), filter_var(), filter_input(), filter_var_array(), filter_input_array(), filter_has_var(), ctype_*(), entre outras várias para manipulação, conversão e validação de tipos e de variáveis.
ARQUIVOS
É cada vez mais comum encontrarmos sistemas que oferecem a possibilidade de enviarmos algum arquivo como uma imagem, um documento pdf, uma planilha para ser importada, um certificado digital para criptografia entre outros, e armazená-lo no servidor. Se você utiliza esse recurso, nunca esqueça de validar os uploads antes de salvar os arquivos no servidor, nem permita que eles sejam acessíveis diretamente através de uma chamada direta proveniente do browser. Dessa forma você evita que arquivos maliciosos sejam armazenados e causem algum dano ao seu servidor ou ao seu sistema.
Vale ressaltar, no entanto, que validar apenas a extensão do arquivo não é suficiente, pois ela pode ser manipulada no momento do envio. Para uma solução completa, é preciso verificar o cabeçalho do arquivo, ou mesmo o conteúdo de dados em posições específicas, veremos isso lá na frente, já que pode ser bem complexo manipular as “ASSINATURAS DE ARQUIVOS” ou seja, partes que podem ser identificadas.
O PHP possui diversas funções que permitem manipulação e verificação de conteúdos, como imagens, pdf, audio entre outros, e extensões que podem manipular determinados tipos, e são especializadas em verificar a assinatura de determinados formatos de arquivos.
Para evitar problema, uma alternativa é proteger as pastas que contêm esses arquivos que foram enviados contra acessos externos. Para isso, basta colocar esses arquivos fora da pasta public_html (ou outra acessível via web) ou adicionar um arquivo .htaccess com as configurações para aquele conteúdo específico nas pastas que quiser proteger.
SENHAS e CONTROLE DE ACESSO
Quando falamos em senhas, uma boa prática é jamais armazenar senhas diretamente no banco de dados, é importante usar funções de hash para esconder o conteúdo diretamente das vistas caso exista um vazamento de dados.
Outra dica, é monitorar as tentativas de falha, por exemplo, ao receber um login válido, porém uma senha errada, você pode salvar e limitar a quantidade de tentativas de senha, por exemplo salvando uma tabela do banco de dados com os nomes de usuários, e antes de tentar validar a senha você conta quantas vezes uma entrada de um mesmo nome está presente, e digamos, se for maior que 5 nem tenta validar, informando que ele fica bloqueado por digamos 1 hora. Uma boa prática também é retardar a mensagem de resposta de senha, ou seja, você usa essa contagem que vai de 0 a 4 e a multiplica por 1000, e antes de passar para a consulta e validação, você adiciona um sleep() com esse valor, ou seja se ele não errou a senha, a resposta é imediata, se ele ja possui erro, a resposta demora tipo 1 segundo.
Qual a utilidade, se o atacante consegue avaliar 100 senha por segundo, com esse retardo na resposta, ele não consegue mais que 2, reduzindo os ataques por força bruta.
SYSTEM ADMIN
Nem todas as falhas de segurança ocorrem no código escrito, portanto, quando tiver em um servidor principalmente um servidor compartilhado, tenha certeza que as diretrizes de segurança do próprio sistema estejam ajustadas de maneira a permitir que a aplicação e os dados estejam seguros.
Você pode utilizar algumas configurações do PHP e do servidor web, porém, ter conhecimento profundo sobre a administração de servidores sejam eles linux ou windows, é a certeza que não só a aplicação php, como outros serviços e portas não estejam abertas e permitam que invasores acesse dados ou explorem essas falhas.
Mantendo-se Atualizado
PHP, como qualquer outro sistema, está sobre constante revisão e melhoramento, a cada versão nova mudanças grandes ou pequenas, ocorrem para melhorar a segurança e reparar falhas, erros de configuração, e outros problemas reportados pelos usuários e por outros programadores, que podem afetar a segurança geral e estabilidade do sistema.
Além de garantir a segurança dos usuários e das transações, um sistema web seguro também deve garantir a integridade do servidor que o hospeda. Por esse motivo, os profissionais da área devem estar sempre preparados e bem informados sobre as principais ameaças e as técnicas que ajudam a evitá-las. E, pode apostar, essa não é uma tarefa tão simples quanto parece: há uma infinidade de procedimentos que podem ser adotados para evitar uma infinidade maior ainda de acessos ou uso indevido de dados e recursos.
Conclusão
Se você chegou até aqui, com certeza você já consegue escrever scripts em PHP que permitam a criação de páginas web dinâmicas, e com conteúdos provenientes de outros site, ou mesmo de bancos de dados. Na próxima aula, vamos falar algumas características e limitações do PHP que vão determinar onde e como ele pode ou deve ser usado, formas de implementar segurança e como você pode utilizar os principais recursos da linguagem em conjunto, para desenvolver aplicações complexas.