Segurança de Sessão no PHP

Posted on abril 15, 2010. Filed under: PHP |

Neste artigo vamos abordar os principais ataques explorados em cima de aplicações PHP que usam sessões. Procurei demonstrar como os ataques são feitos e como podemos nos previnir deles. Os principais ataques são chamados de session fixation e session hijacking. Além desses, temos que ter certos cuidados quando trabalhamos com sessões em hosts compartilhados (shared hosting).

Antes de começarmos vamos a um pequeno resumo do o que é e como funciona sessões no php.

Sessões permitem preservar dados através de acessos consecutivos. Isto permite que você crie aplicações mais costumizadas para o usuário. Quando um visitante acessa sua aplicação, é associado um identificador único à ele que fica registrado e um cookie no seu navegador. Nos acessos consecutivos, o visitante envia o cookie, fazendo com que dessa forma ele possa ser identificado na sessão. É possível registrar qualquer número de variáveis que serão preservadas enquanto a sessão existir.

Session Fixation

Quando o usuário acessa um página de sua aplicação em que é chamada a função session_start(), uma nova sessão é criada para o usuário. O PHP gera um identificador randômico referente a conexão do usuário e então envia um header Set-Cookie para ele contendo o identificador. Por padrão o nome do cookie é PHPSESSID. Nas requisições seguintes o navegador do usuário envia o cookie para o servidor e dessa a forma a aplicação é capaz de idenficá-lo.

Porém é possível setar o identificador da sessão manualmente através da query string. Consequentemente todas as próximas requisições irão usar esse identificador. Para explorar essa vulnerabiliade o atacante cria uma página contendo um link para o site no qual deseja obter acesso à sessão da vítima. Exemplo:

<a href="http://www.dominio-exemplo.com?PHPSESSID=987654321">Acessar</a>

Se o usuário clica no link, acessa o site e loga em alguma área restrita ele estará usando o identificador criado pelo atacante. Dessa forma é possível que o atacante obtenha acesso a conta do usuário logado no site, simplesmente usando o identificador criado por ele.
Este ataque é conhecido como Session Fixation.

Nas versões mais recentes do php as opções do php.ini: session.use_trans_sid e session.use_only_cookies vem setadas de certa forma que impedem que o ataque aconteça.

Para testar a vulnerabilidade abaixo, você deve setar a opção session.use_trans_sid para On e session.use_only_cookies para Off no php.ini. O desenvolvedor que pretende possiblitar a utilização de sessão em navegadores que tem cookies desabilitados devem setar as diretivas dessa maneira. Acredito que hoje em dia é muito raro usuários que estão navegando com a opção de cookies desativada. Portanto o php já vem configurado de modo “seguro”.

Testando a vulnerabilidade:

<?php
session_start();

if (!array_key_exists('visitas', $_SESSION))
    $_SESSION['visitas'] = 1;
else
    $_SESSION['visitas']++;

echo $_SESSION['visitas'];
?>

Quando você acessar essa página pela primeira vez você irá visualizar o número 1. Para cada acesso subsequente você verá o número sendo incrementado por mais um.

Para demonstrar a vulnerabilidade, feche todos os browsers, delete os cookies e depois abra novamente, acrescentando ?PHPSESSID=654321 no final da URL. Na primeira vez você verá o número 1. A partir de um outro computador ou um outro navegador, acesse a página acrescentando ?PHPSESSID=654321 no final da URL. Dessa vez, você não verá o número 1, mas sim o número subsequente ao último acesso realizado na sessão. O que aconteceu aqui é que o segundo “usuário” obteve acesso à sessão do primeiro usuário.

Para se proteger contra o ataque no nível de aplicação é simples. Toda vez que o usuário autenticar ou mudar o seu previlégio simplesmente gere novamente o identificador da sessão. Isso é feito através do comando session_regenerate_id(). Dessa forma o usuário irá utilizar um identificador novo quando logar no site, diferente do inicial quando acessou a página pela primeira vez. O atacante não terá como saber o novo identificador caso ele use a técnica de fixar o identificador da sessão. Exemplo:

<?php
/* proteger contra session fixation. Gerar novo
  identificador ao logar no sistema. */

if (usuario_autenticado()) {
    session_regenerate_id();
}
?>

Sesssion Hijacking

Este ataque é mais difícil de se proteger porém mais difícil de ser explorado também. Supondo que você use o session_regenerate_id() conforme explicado anteriormente, gerando um novo identificador da sessão. O que aconteceria se um usuário malicioso obtesse esse identificador ? Ele poderia facilmente obter acesso à sua sessão. Mas como um atacante poderia ter acesso ao identificador da minha sessão ? Devido ao identificador da sessão estar gravado em um cookie no navegador, o usuário malicioso pode explorar alguma vulnerabilidade do browser ou obter os cookies do navegador através de um ataque muito conhecido chamado de XSS (Cross-Site Scripting). Para que o ataque XSS sejá possível, o site em que a vítima esta logado deverá ter essa vulnerabilidade em alguma das páginas do site. Por incrível que pareça essa vulnerabilidade é fácil de previnir, porém muitos sites estão vulneráveis devido a falta de de cultura do desenvolvedor sobre segurança.

Uma maneira que pode aumentar a segurança e ajudar a previnir session hijacking é checar algum dos headers da requisição. Um header sugerido é o User-Agent, principalmente porquê ele não muda entre as requisições de uma sessão. Você deverá guardar os dados deste header em uma varíáveis de sessão no primeiro acesso do usuário e depois checá-lo nos acessos subsequentes. Uma maneira de dificultar ainda mais o trabalho de um atacante é gravar um hash md5 do valor do header User-Agent. Segue o exemplo:

<?php

session_start();

if (array_key_exists('HTTP_USER_AGENT', $_SESSION))
{
    if ($_SESSION['HTTP_USER_AGENT'] !=
        md5($_SERVER['HTTP_USER_AGENT']))
    {
      /* Acesso inválido. O header User-Agent mudou
       durante a mesma sessão. */
      exit;
    }
}
else
{
  /* Primeiro acesso do usuário, vamos gravar na sessão um
   hash md5 do header User-Agent */
    $_SESSION['HTTP_USER_AGENT'] =
        md5($_SERVER['HTTP_USER_AGENT']);
}
?>

Segurança de sessão em Hosts Compartilhados

Um host compartilhado não tem a mesma segurança que um host dedicado, por isso devemos tomar algumas precauções quando trabalhamos neste ambiente.

Uma das causas das vulnerabilidades em hosts compartilhados é que os dados de sessão são armazenados em um mesmo diretório para todos os sites. Por padrão o PHP armazena os dados da sessão no diretório /tmp.

Por sorte a permissão dos arquivos de sessão permitem que somente o usuário do webserver possa ler e escrever nos arquivos. Geralmente o servidor web roda com o usuário nobody ou outro usuário definido no httpd.conf, geralmente chamado de www. Usuários comuns logados no sistema não conseguirão ler estes dados.

Porém quando se trata de uma aplicação php que roda a partir do servidor web, é possível que o programador escreva um script para acessar o diretório /tmp e ler os arquivos de sessão. Isso é possível pois o php rodando no servidor web tem permissão de acesso aos arquivos criados pelo usuário do serviço http.

Por muito tempo umas das maneiras do administrador do host de resolver problemas com segurança era usar a diretiva safe_mode. Porém o safe_mode será removido no PHP6 e não é mais aconselhável o seu uso.

Para protegermos o diretório /tmp no lado do servidor, umas das opções é utilizar a diretiva open_basedir na configuração do apache (httpd.conf) ou no php.ini. Com o open_basedir você pode limitar os diretórios onde o programador poderá abrir ou incluir arquivos. (fopen() / include). Exemplo:

<Directory /var/www>
   php_admin_value open_basedir "/var/www"
</Directory>

Dessa maneira estamos permitindo que arquivos sejam abertos somente no diretório /var/www. O diretório /tmp estará protegido.
Limitar diretórios com open_basedir não é tudo. Para uma maior segurança devemos desabiltar funções do php que permitem a execução de programas internos. Para isso use a diretiva disable_functions e disable_classes. Exemplo no php.ini:

; Desabilitar funções
disable_functions = exec,passthru,shell_exec,system

;Desabilitar classes
disable_classes = DirectoryIterator,Directory

Caso você seja um programador que deseja uma segurança maior do que somente a segurança oferecida pelo serviço de host compartilhado você pode escrever um código para armazenar os dados da sessão em outra local além do diretório /tmp. Por exemplo, você pode gravar os dados em uma tabela do banco de dados. Para isso você precisará usar a função session_set_save_handler, e escrever uma função para cada ação relacionanda à sessão.
Essa função possibilita que você tenha o controle dos dados da sessão em suas mãos e não nas mãos da administração do seu host compartilhado. Exemplo:

session_set_save_handler(
  '_open', '_close', '_read', '_write', '_destroy', '_clean');

Você deverá escrever uma função para cada ação: ‘_open’, ‘_close’, ‘_read’, ‘_write’, ‘_destroy’, ‘_clean’. Para maiores informações de como essas funções devem ser implementadas acesso o link: http://www.php.net/manual/en/function.session-set-save-handler.php

Conclusão

O objetivo deste artigo é fornecer práticas de segurança relacionada à sessões. Existem muitos sites vulneráveis devido a falta de informação de como devemos proteger nossas aplicações. Devemos estar sempre buscando práticas seguras e aplicá-las. Quanto mais nos conscientizarmos sobre os ataques e suas respectivas prevenções melhor.

Make a Comment

Deixe uma resposta para pasquati Cancelar resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

10 Respostas to “Segurança de Sessão no PHP”

RSS Feed for Pasqua Tecnologia Comments RSS Feed

Parabéns, gostei muito das dicas, sou programador a pouco tempo e me preocupo com a segurança.
Vou tentar implantar as sugestões nos meus scripts.

Ola @Pasquati, excelente materia, um resumo dos fatores mais importantes para segurança de sessões, Parabens.

É importante enfatizar…
Criptografar apenas o cabeçalho em uma hash md5 que ja tem volta nao é tao seguro quanto se parece!

Talvez fosse bem interessante associar o IP e alguma palavra a escolha do programador!
E tbm, colocar uma hash que nao se tenha volta 😉

valeu cara, usar phpids é legal, mas conhecer o modo de criar a segurança é melhor ainda.Vlw

Mto bom.
Li sobre o assunto em vários sites e esse aqui sem dúvida foi o mais esclarecedor.
Parabéns, mto bom mesmo

Obrigado! 🙂

Bom dia gostei do artigo vou aplicar. Quando faço o login não reconhece e nem exibe erro na Session aonde devo verificar, sendo que na base local funciona perfeitamente. Obrigado

Bom dia!!

Pode ser que a exibição de erros na tela esteja desabilitado no php.ini. Insira o códigpo abaixo no ínicio do seu script php:

ini_set(‘display_errors’, 1);

Ou altere essa opção no próprio php.ini do seu servidor.

Boa sorte.

caramba muito bom! Parabéns!

Obrigado Adriel!


Where's The Comment Form?

Liked it here?
Why not try sites on the blogroll...

%d blogueiros gostam disto: