ScotlandPHP 2016

Sessões e segurança

Links externos: » Session fixation

Gerenciamento de sessões HTTP é o núcleo de segurança na web. Toda provenção deve ser adotada para garantir a segurança. Desenvolvedores devem habilitar/utilizar as configurações apropriadamente.

  • session.cookie_lifetime=0. 0 tem um sentido especial. Ele diz ao browser para não guardar os cookies no armazenamento permanente. Portanto, quando o browser é encerrado, o cookie com ID de sessão é deletado imediatamente. Se o desenvolvedor definir valor diferente de 0, pode permitir que outros usuários utilizem o ID da sessão. A maioria das aplicações devem utilizar "0" para esta configuração. Se o recurso de login automático é necessário, implemente seu próprio recurso de login automático de forma segura. Não utilize o ID da sessão para isto.
  • session.use_cookies=On and session.use_only_cookies=On. Apesar de cookies HTTP terem alguns problemas, cookies é a forma preferida para gerenciar o ID de sessão. Utilize apenas cookies para gerenciamento do ID de sessão quando possível. A maioria das aplicações devem utilizar cookies para o ID de sessão.
  • session.use_strict_mode=On. Isto previne o módulo de sessão de utilizar ID de sessão não inicializado. Em outras palavras, o módulo de sessão aceita apenas ID de sessão válido gerado pelo módulo de sessão. ID de sessão fornecidos pelo usuário são rejeitados. Injeção de ID de sessão pode ser feito injetando cookies via JavaScript permanentemente ou temporariamente. Quando sessão transparente está habilitada, o ID de sessão pode ser injetado via query string ou parâmetro de formulário. Não há razão para aceitar ID de sessão fornecida pelo usuário, a maioria das aplicações não devem aceitar ID de sessão não inicializados fornecidos pelo usuário.
  • session.cookie_httponly=On. Impede que o JavaScript tenha acesso aos cookies de sessão. Esta configuração previne o roubo de cookies por injeção de JavaScript. É possível usar ID de sessão como proteção CSRF, mas não é recomendado. Por exemplo, o código HTML pode ser salvo e enviado para outros usuários. O desenvolvedor não deve adicionar o ID de sessão nas páginas para manter a segurança. Quase todas as aplicações devem utilizar o atributo httponly para cookie de ID de sessão.
  • session.cookie_secure=On. Permite acesso ao cookie de ID de sessão apenas quando o protocolo é HTTPS. Se seu web site é todo em HTTPS, você deve habilitar esta configuração. O uso de HSTS deve ser considerado para sites completamentes em HTTPS.
  • session.gc_maxlifetime=[choose smallest possible]. GC trabalha com probabilidade. Esta configuração não garante que sessões antigas serão apagadas. Alguns manipuladores de gravação de sessão não utilizam esta configuração. Veja a documentação para manipuladores de gravação de sessão para detalhes. Apesar do desenvolver não poder depender desta configuração, configurá-la para o menor valor possível é recomendado. Ajuste session.gc_probability e session.gc_divisor para que sessões obsoletas sejam deletadas na frequência apropriada. Se o recurso de login automático é necessário, implemente seu próprio recurso de login automático de forma segura. Não utilize ID de sessão com longa vida para isto.
  • session.use_trans_sid=Off. O uso do gerenciamento transparente de ID de sessão não é proibido. Ele deve ser usado quando necessário. Contudo, desabilitar o gerenciamento transparente melhora a segurança geral do ID de sessão ao remover a possibilidade de injeção e vazamento do ID de sessão.
  • session.referer_check=[your originating URL] Quando session.use_trans_sid está habilitado, o uso desta configuração é recomendada se for possível. Ela reduz o risco de injeção de ID de sessão. Se seu site é http://example.com/, então defina-o como http://example.com/. Note que quando HTTPS é usado, o browser não enviará o cabeçalho com o referrer. O browser também pode não enviar o cabeçalho com o referrer por configuração. Portanto, esta configuração não é uma medida de segurança confiável.
  • session.cache_limiter=nocache. Certifique-se de que conteúdo HTTP não sejam salvos em cache para sessão autenticada. Permita cache apenas quando o conteúdo não é privado. Caso contrário, os conteúdos podem ser expostos. "privado" pode ser usado se o conteúdo HTTP não inclui dados sensíveis. Note que "privado" pode deixar informações privadas salvas em cache por clientes compartilhados. "public" pode ser usado apenas quando o conteúdo HTTP não contém informação privada.
  • session.hash_function="sha256". Funções de hash mais fortes irão gerar ID de sessão mais fortes. Apesar de colisão no hash ser pouco provável até mesmo com hash MD5, desenvolvedores devem usar funções de hash SHA-2 ou posterior para a tarefa. Desenvolvedores podem utilizar hashes mais fortes, como sha384 e sha512.

O módulo de sessão não pode garantir que a informação que você guarda em uma sessão será visto somente pelo usuário que criou a sessão. Medidas adicionais de segurança devem ser tomadas para proteger ativamente a integridade da sessão, dependendo do valor dos dados.

Avalie a importância dos dados carregados pelas suas sessões e tome medidas adicionais de proteção -- isto normalmente vem com um preço, menos conveniência para o usuário. Por exemplo, se você quiser proteger os usuários de táticas simples de engenharia social, você deve ativar session.use_only_cookies. Neste caso, os cookies devem estar ativados incondicionalmente do lado do usuário ou as sessões não irão funcionar.

Existem várias maneiras de vazar um ID de sessão para terceiros. Um ID de sessão vazado permite a terceiros acessar todos os recursos que estão associados ao ID específico. Primeiro, URLs carregando IDs de sessão. Se você criar um link com um site externo, a URL incluindo o ID de sessão pode ser guardada nos logs do site externo. Segundo, um ataque mais ativo pode capturar seu tráfego de rede. Se ele não for criptografado, os IDs de sessão irão passar como texto simples pela rede. A solução aqui é implementar SSL em seu servidor e tornar obrigatório para os usuários. HSTS deve ser usado para isto.

Desde o PHP 5.5.2, session.use_strict_mode está disponível. Quando estiver habilitado e o módulo de gravação o suportar, ID de sessão não inicializado é rejeitado e um novo ID de sessão é criado. Isso protege de ataques que forçam os usuários a utilizarem ID de sessão conhecidos. Atacantes podem colar links ou enviar e-mails contendo o ID de sessão. Por exemplo, http://example.com/page.php?PHPSESSID=123456789 Se session.use_trans_sid estiver habilitado, a vitima iniciará sessão usando o ID de sessão fornecido pelo atacante. session.use_strict_mode diminui o risco.

Apesar de session.use_strict_mode diminuir o risco da adoção do gerenciamento de sessão, o atacante pode forçar os usuários a utilizarem um ID de sessão inicializado criado pelo atacante. Tudo o que o atacante precisa fazer é iniciar o ID de sessão antes do ataque e mantê-lo ativo.

Cookie de ID de sessão pode ser definido com os atributos domain, path, httponly e secure. Existe uma precedência definida pelo browser. Com o uso da precedência, o atacante pode definir o ID de sessão que poderia ser usado permanentemente. O uso de session.use_only_cookies não resolverá essa questão. session.use_strict_mode diminui o risco. Com session.use_strict_mode=On, ID de sessão não inicializado não será aceito. O módulo de sessão cria um novo ID de sessão sempre que o ID de sessão não é inicializado pelo módulo de sessão. Isso poderia resultar em um ataque DoS para a vítima, mas DoS ainda é melhor do que uma conta exposta.

session.use_strict_mode é uma boa forma de diminuir riscos, mas não é o suficiente para sessão autenticada. Desenvolvedores devem usar session_regenerate_id() para autenticação. session_regenerate_id() deve ser chamada antes de definir informações de autenticação em $_SESSION. session_regenerate_id() garante que a nova sessão contém informação de autenticação armazenadas apenas em uma nova sessão. Isto é, erros durante o processo de autenticação podem salvar informações de autenticação em sessão antiga.

Executar a função session_regenerate_id() poderia resultar em ataque DoS, da mesma forma que use_strict_mode=On. No entanto, DoS ainda é melhor do que uma conta exposta. ID de sessão deve ser renovado pelo menos quando o usuário fizer a autenticação. A renovação do ID de sessão reduz o risco do roubo de ID de sessão, deste modo ela deve ser executada periodicamente. O desenvolvedor não deve depender da expiração do ID de sessão. Atacantes podem acessar o ID de sessão da vítima periodicamente para impedir que ele expire. Desenvolvedores devem implementar seus próprios meios de expiração para sessões antigas.

Note que session_regenerate_id() não apaga sessão antiga por padrão. Sessão antiga de autenticação pode ficar disponível para uso. Se o desenvolvedor quiser prevenir que sessão de autenticação antiga seja usada por alguém, o desenvolvedor deve destruir a sessão definindo delete_old_session como TRUE. Contudo, a eliminação de sessão antiga tem efeitos colaterais não desejados. A sessão pode desaparecer quando houver conexões concorrentes em aplicações web e/ou quando a rede estiver instável. Ao invés de apagar a sessão antiga imediatamente, pode ser definido um tempo de expiração curto na $_SESSION. Se o usuário acessar com uma sessão obsoleta (uma sessão expirada), negue o acesso.

session.use_only_cookies e o uso adequado de session_regenerate_id() poderiam causar DoS individual. Quando este for o caso, pode ser solicitado aos usuários que remova os cookies e alertá-los de que pode haver problemas de segurança. Atacantes podem definir cookies maliciosos via aplivações web vulneráveis (isto é, injeção de JavaScript), plugins vulneráveis/malicosos para browsers, etc.

Desenvolvedores não devem utilizar ID de sessão de longa vida para login automático, pois aumenta o risco de roubo de sessão. O login automático deve ser implementado pelo desenvolvedor. Use chaves de hash descartáveis como chaves para o login automático através de cookies. Use hash mais forte que SHA-2. Por exemplo, SHA-256 ou maior com dados aleatórios de /dev/urandom ou similar. Se o usuário não estiver autenticado, verifique se a chave descartável de login automática é válida ou não. Se a chave é válida, autentique o usuário e defina uma nova chave de hash descartável. A chave para login automático é uma chave de autenticação de longa duração, ela deve estar o mais protegida possível. Use os atributos path/httponly/secure dos cookies para proteção. O desenvolvedor deve implementar o recurso que desabilita o login automático e remove chaves desnecessárias dos cookies dos usuários.

add a note add a note

User Contributed Notes 5 notes

up
47
Anurag Jain
7 years ago
Websites which have sensitive information need to be patched to ensure its not exploited because of session issues.

In earlier versions of apache cookie reliability was not assumed and hence the default method was always using url-rewrite which meant every url link, every form submission etc would have a PHPSESSID=<sessionid> passed along to inform the server about the active session. New versions have turned this off using

session.use_trans_sid = 0

in the /etc/php5/apache2/php.ini file.

Reasons?
Well one might safe the offline page as a bookmark or pass the link across to others not realizing that the session id information is also sent. So someone who quickly accesses these pages could possible get logged on, this was also true wrt search engines, and I guess in some cases it being seen as duplicate content as the same page will have a different session id every time the robots scan the website.

But having this set does not mean you are protected. Let me explain.
What prevents me from presetting the session id! Assume there is a banking site www.example.com which has a login screen at www.example.com/login.php
I can send you can email with a link to the bank site as http://www.example.com/login.php?PHPSESSID=12345
When you click on the link it presents the session id as 12345 rather then asking the server to generate a new one. This is called session fixation. Keep in mind even with session.use_trans_sid = 0 this will work as this sets it only not to use url-rewrite. To prevent this altogether set session.use_only_cookies = 1 which ensures that only cookies will be used, but this could cause problems when dealing with transaction which involve switch sites, i.e. siteA forwards to site B for payment which forwards to siteA for thank you, in which case a phpsessid inform might be used to revive the old session.

A good approach would always be to at the login screen and immediately post login to force a new session id generated using random numbers

session_start();
$newsessid = somerandomnumberfunction();
session_id($newsessid);

you can also use session_regenerate_id() function to generate a new id

session_start();
session_regenerate_id();

Also its always good to ensure every valid session is checked against an ip. One good method is to store the session id and remote ip information in a table, or better store the ip as a session variable itself, once the user logs in and ensure that this is continued for remaining pages for security. This ofcourse wont work when users use the same office or shared network as the ip to the outside world is the same.

https is always a good idea for sensitive sites, but keeping it persistent for all pages which use session is important if you really want a foolproof system else anyone can always sniff your packets.

So to quickly go through the bits

- set session.use_trans_sid = 0 in /etc/php5/apache2/php.ini file.
- Ensure you always use a new self generated session id on successful login attempt.
- Try setting session.use_only_cookies = 1 and check if all works fine.
- Use https throughout to ensure no one can sniff your session id.
- Store session id, remote IP information and compare for successive pages
up
10
bmearns at ieee dot org
7 years ago
In addition to ip-address binding not always being effective, it can also prevent users connecting through a proxy-pool from even being able to use your site. In such a scenario, a person's IP address may very well change with every access.

If you're handling anything remotely secure, the only safe option is HTTPS. If the data doesn't need to be that secure, than you should not allow a high-jacked session to do too much damage. Basically, don't assume that a person really is who they pretend to be just because the session says a person authenticated with a username and password: it may have been that person who logged in, but that doesn't mean it's still that person. So if you're going to do something like change passwords or something, require them to authenticate again on the same form.

Of course this needs to be done in some secure way, as well, so that the password is not just floating over the network. A good way to do this is sending a nonce (number-used-once) along with the form and some javascript to concatenate the nonce to the password, then perform a cryptographic hash of the combined string. The resulting valid-once "password" should then be sent instead of the plain text password. To be effective, you also need to prevent people from using the form if they don't have JavaScript enabled. You can do this by disabling the form fields in HTML, then re-enabling them in JavaScript.
up
0
justin at fatbird dot ca
7 years ago
IP checking is a sometimes useful feature with two limitations that are important to be aware of:

1. Anyone surfing behind a proxy (e.g., someone at work) will provide the proxy's IP, not his own.  Session ID replay attacks will not be prevented by IP checking for an attacker on the user's side of the proxy.

2. If the PHP application is behind a reverse proxy, the reverse proxy's IP address will be the only request IP seen by PHP, so IP checking is useless.
up
1
Olle Bergkvist
7 years ago
It is also quite important to (somehow) make sure that the cookies you're setting (including the session cookie) is only visible to the site that created it (or to other trusted sites only).

If the cookie's path is set to '/' (the whole domain), then any website on the same domain (might be lots of websites) _will_ get the cookie through HTTP headers and could possibly hijack your session.

One slightly acceptable protection would be to lock a session to one IP adress.
up
-3
JonathanFeller at NOSPAMgmx dot ch
7 years ago
Perhaps, you would also like to timeout a session after some idle time. I noticed that session.gc_maxlifetime is not suitable for this. So I used this code to do the job:

<?php
if (!isset($_SESSION['timeout_idle'])) {
   
$_SESSION['timeout_idle'] = time() + MAX_IDLE_TIME;
} else {
    if (
$_SESSION['timeout_idle'] < time()) {   
       
//destroy session
   
} else {
       
$_SESSION['timeout_idle'] = time() + MAX_IDLE_TIME;
    }
}
?>
To Top