Fixação / seqüestro de sessões PHP

145

Estou tentando entender mais sobre Fixação e seqüestro de sessões PHP e como evitar esses problemas. Estive lendo os dois artigos a seguir no site de Chris Shiflett:

No entanto, não tenho certeza se estou entendendo as coisas corretamente.

Para ajudar a impedir a fixação da sessão, basta chamar session_regenerate_id (true); depois de fazer login em alguém com êxito? Eu acho que entendi isso corretamente.

Ele também fala sobre o uso de tokens transmitidos em URLs via $ _GET para impedir o seqüestro de sessões. Como isso seria feito exatamente? Suponho que quando alguém fizer login, você gerar seu token e armazená-lo em uma variável de sessão, em cada página você compararia essa variável de sessão com o valor da variável $ _GET?

Esse token precisaria ser alterado apenas uma vez por sessão ou no carregamento de cada página?

Também é uma boa maneira de impedir o seqüestro sem ter que passar um valor nos urls? isso seria muito mais fácil.

me2
fonte
Talvez você possa adicionar links para as páginas em que você encontrou essas recomendações.
Gumbo

Respostas:

219

Ok, existem dois problemas separados, mas relacionados, e cada um é tratado de maneira diferente.

Fixação de Sessão

É aqui que um invasor define explicitamente o identificador de uma sessão para um usuário. Normalmente, no PHP, isso é feito, fornecendo a eles um URL http://www.example.com/index...?session_name=sessionid. Depois que o invasor fornece o URL ao cliente, o ataque é o mesmo que um ataque de seqüestro de sessão.

Existem algumas maneiras de impedir a fixação da sessão (faça todas elas):

  • Defina session.use_trans_sid = 0no seu php.iniarquivo. Isso instruirá o PHP a não incluir o identificador na URL e a não ler a URL para identificadores.

  • Defina session.use_only_cookies = 1no seu php.iniarquivo. Isso instruirá o PHP a nunca usar URLs com identificadores de sessão.

  • Gere novamente o ID da sessão sempre que o status da sessão for alterado. Isso significa qualquer um dos seguintes:

    • Autenticação de usuário
    • Armazenando informações confidenciais na sessão
    • Alterando qualquer coisa sobre a sessão
    • etc ...

Seqüestro de Sessão

É aqui que um invasor detém um identificador de sessão e pode enviar solicitações como se fosse esse usuário. Isso significa que, como o invasor possui o identificador, eles são praticamente indistinguíveis do usuário válido em relação ao servidor.

Você não pode impedir diretamente o seqüestro de sessão. No entanto, você pode executar etapas para torná-lo muito difícil e difícil de usar.

  • Use um identificador de hash de sessão forte: session.hash_functionin php.ini. Se PHP <5.3, defina-o como session.hash_function = 1SHA1. Se PHP> = 5.3, defina-o como session.hash_function = sha256ou session.hash_function = sha512.

  • Envie um hash forte: session.hash_bits_per_characterin php.ini. Defina isso para session.hash_bits_per_character = 5. Embora isso não torne mais difícil o crack, faz diferença quando o atacante tenta adivinhar o identificador da sessão. O ID será mais curto, mas usa mais caracteres.

  • Defina uma entropia adicional com session.entropy_filee session.entropy_lengthno seu php.iniarquivo. Defina o primeiro como session.entropy_file = /dev/urandome o último como o número de bytes que serão lidos no arquivo de entropia, por exemplo session.entropy_length = 256.

  • Mude o nome da sessão do PHPSESSID padrão. Isso é feito chamando session_name()com seu próprio nome de identificador como o primeiro parâmetro antes da chamada session_start.

  • Se você é realmente paranóico, também pode alternar o nome da sessão, mas lembre-se de que todas as sessões serão automaticamente invalidadas se você alterar isso (por exemplo, se você depender da hora). Mas, dependendo do seu caso de uso, pode ser uma opção ...

  • Gire seu identificador de sessão com frequência. Eu não faria isso a cada solicitação (a menos que você realmente precise desse nível de segurança), mas em um intervalo aleatório. Você deseja alterar isso com frequência, pois, se um invasor seqüestrar uma sessão, não deseja que ele possa usá-la por muito tempo.

  • Inclua o agente do usuário$_SERVER['HTTP_USER_AGENT'] na sessão. Basicamente, quando a sessão começar, armazene-a em algo parecido $_SESSION['user_agent']. Em seguida, em cada solicitação subsequente, verifique se ele corresponde. Observe que isso pode ser falsificado, por isso não é 100% confiável, mas é melhor do que não.

  • Inclua o endereço IP do usuário$_SERVER['REMOTE_ADDR'] na sessão. Basicamente, quando a sessão começar, armazene-a em algo parecido $_SESSION['remote_ip']. Isso pode ser problemático em alguns ISPs que usam vários endereços IP para seus usuários (como a AOL costumava fazer). Mas se você usá-lo, será muito mais seguro. A única maneira de um invasor falsificar o endereço IP é comprometer a rede em algum momento entre o usuário real e você. E se eles comprometem a rede, eles podem fazer muito pior do que um seqüestro (como ataques MITM, etc).

  • Inclua um token na sessão e no lado do navegador que você incrementa e compara com frequência. Basicamente, para cada solicitação, faça $_SESSION['counter']++no lado do servidor. Faça também algo em JS no lado do navegador para fazer o mesmo (usando um armazenamento local). Em seguida, quando você envia uma solicitação, simplesmente pegue um token de não aceitação e verifique se o nonce é o mesmo no servidor. Ao fazer isso, você poderá detectar uma sessão seqüestrada, pois o atacante não terá o contador exato ou, se o fizer, você terá 2 sistemas transmitindo a mesma contagem e pode dizer que uma é forjada. Isso não funcionará para todos os aplicativos, mas é uma maneira de combater o problema.

Uma nota sobre os dois

A diferença entre Fixação de sessão e Seqüestro é apenas sobre como o identificador da sessão está comprometido. Na fixação, o identificador é definido como um valor que o invasor conhece de antemão. No seqüestro, é adivinhado ou roubado do usuário. Caso contrário, os efeitos dos dois serão os mesmos quando o identificador for comprometido.

Regeneração de ID de Sessão

Sempre que você gerar novamente o identificador de sessão usando session_regenerate_ida sessão antiga, ele deve ser excluído. Isso acontece de maneira transparente com o manipulador de sessão principal. No entanto, alguns manipuladores de sessão personalizados usandosession_set_save_handler() isso não fazem isso e estão abertos a ataques a identificadores de sessão antigos. Certifique-se de que, se você estiver usando um manipulador de sessão personalizado, acompanhe o identificador que abrir, e se não for o mesmo que salvar, exclua (ou altere) explicitamente o identificador no antigo.

Usando o manipulador de sessão padrão, você está bem apenas chamando session_regenerate_id(true). Isso removerá as informações antigas da sessão para você. O ID antigo não é mais válido e fará com que uma nova sessão seja criada se o invasor (ou qualquer outra pessoa) tentar usá-lo. Tenha cuidado com os manipuladores de sessão personalizados ...

Destruindo uma sessão

Se você pretende destruir uma sessão (por exemplo, no logout), certifique-se de destruí-la completamente. Isso inclui desarmar o cookie. Usando session_destroy:

function destroySession() {
    $params = session_get_cookie_params();
    setcookie(session_name(), '', time() - 42000,
        $params["path"], $params["domain"],
        $params["secure"], $params["httponly"]
    );
    session_destroy();
}
ircmaxell
fonte
4
Usar 5 em vez de 4 bits por caractere não altera a “força” de forma alguma (o que “força” significa neste caso). Mas, embora seus pontos sejam aconselháveis ​​em geral, eles não possuem alguns detalhes importantes. Por exemplo, o que acontece com a sessão associada ao ID de sessão antigo ou como uma sessão com um ID de sessão antigo deve ser tratada depois que ela é inválida.
Gumbo
2
@attal: Não, esse é o ponto. session_regenerate_idnão invalida a sessão que ainda está associada ao ID antigo; somente se o parâmetro delete_old_session estiver definido como true, a sessão será destruída. Mas e se um invasor iniciou essa regeneração de ID?
Gumbo
6
Eu discordo da regeneração da sessão toda vez que você altera uma variável de sessão, ela deve ser feita apenas no login / logout. A verificação do agente do usuário também não faz sentido e a verificação do REMOTE_ADDR é problemática. Uma coisa que eu gostaria de acrescentar é session.entropy_file = /dev/urandom. A geração de entropia interna do PHP é comprovadamente extremamente fraca e o pool de entropia fornecido por / dev / random ou / dev / uranom é o melhor que você pode obter em um servidor da Web sem uma configuração de hardware.
rook
4
Além disso, você deve adicionar session.cookie_httponlye session.cookie_secure. O primeiro ajuda a impedir o xss (mas não é perfeito). A segunda é a melhor maneira de parar o OWASP A9 ...
reveja
4
Não entenda uma resposta tão boa, mas falta a parte mais importante: use SSL / HTTPS. O incremento do contador é uma fonte de problema com várias solicitações rapidamente uma após a outra, um usuário atualiza uma página duas vezes ou pressiona os botões de envio duas vezes. A solução de endereço IP é um problema hoje em dia com todos os usuários móveis e em constante mudança de IP. Você pode olhar para o primeiro conjunto do IP, mas ainda está pedindo problemas. O melhor é impedir a descoberta do ID da sessão e usar SSL / HTTPS.
Sanne
37

Os dois ataques de sessão têm o mesmo objetivo: obter acesso a uma sessão legítima de outro usuário. Mas os vetores de ataque são diferentes:

Nos dois ataques, o ID da sessão é o dado sensível em que esses ataques estão focados. Portanto, é o ID da sessão que precisa ser protegido para um acesso de leitura (Session Hijacking) e um acesso de gravação (Session Fixation).

A regra geral de proteção de dados confidenciais usando HTTPS também se aplica nesse caso. Além disso, você deve fazer o seguinte:

Para impedir ataques de fixação de sessão , verifique se:

Para impedir ataques de seqüestro de sessão , verifique se:

Para impedir os dois ataques de sessão, verifique se:

  • para aceitar apenas sessões que seu aplicativo iniciou. Você pode fazer isso imprimindo uma sessão na inicialização com informações específicas do cliente. Você pode usar o ID do agente do usuário, mas não o endereço IP remoto ou qualquer outra informação que possa mudar entre as solicitações.
  • para alterar o ID da sessão usando session_regenerate_id(true)após uma tentativa de autenticação ( trueapenas com êxito) ou uma alteração de privilégios e destruir a sessão antiga. (Certifique-se de armazenar as alterações de $_SESSIONuso session_write_close antes de gerar novamente o ID se desejar preservar a sessão associada ao ID antigo; caso contrário, apenas a sessão com o novo ID será afetada por essas alterações.)
  • para usar uma implementação de expiração de sessão adequada (consulte Como expiro uma sessão PHP após 30 minutos? ).
quiabo
fonte
Postagem incrível, especialmente a última seção.
Mattis 29/05
6

Os tokens mencionados são um número "nonce" - usado uma vez. Eles não precisam necessariamente ser usados ​​apenas uma vez, mas quanto mais tempo forem usados, maiores serão as chances de que o nonce possa ser capturado e usado para seqüestrar a sessão.

Outra desvantagem dos nonces é que é muito difícil criar um sistema que os use e permita várias janelas paralelas no mesmo formulário. por exemplo, o usuário abre duas janelas em um fórum e começa a trabalhar em duas postagens:

window 'A' loads first and gets nonce 'P'
window 'B' loads second and gets nonce 'Q'

Se você não tem como rastrear várias janelas, você terá armazenado apenas um nonce - o da janela B / Q. Quando o usuário envia sua postagem da janela A e passa no nonce 'P', esse sistema rejeita a postagem como P != Q.

Marc B
fonte
Então, o que isso tem a ver com a fixação da sessão?
rook
2
Ele tem um ponto válido, especialmente no campo de usar muitas solicitações AJAX simultaneamente.
DanielG
2

Não li o artigo de Shiflett, mas acho que você entendeu algo errado.

Por padrão, o PHP passa o token da sessão na URL sempre que o cliente não aceita cookies. Caso contrário, no caso mais comum, o token da sessão é armazenado como um cookie.

Isso significa que, se você colocar um token de sessão na URL, o PHP o reconhecerá e tentará usá-lo posteriormente. A fixação da sessão ocorre quando alguém cria uma sessão e depois engana outro usuário para compartilhar a mesma sessão, abrindo uma URL que contém o token da sessão. Se o usuário se autenticar de alguma forma, o usuário mal-intencionado conhece o token da sessão de um autenticado, que pode ter privilégios diferentes.

Como tenho certeza de que Shiflett explica, a coisa mais comum a fazer é regenerar um token diferente cada vez que os privilégios de um usuário são alterados.

Andrea
fonte
Para adicionar isso, destrua as sessões abertas anteriormente, pois elas ainda serão válidas com as permissões de usuário existentes.
22711
0

Sim, você pode impedir a fixação da sessão, regenerando o ID da sessão uma vez no login. Dessa forma, se o invasor não souber o valor do cookie da sessão recém-autenticada. Outra abordagem que interrompe totalmente o problema é definida session.use_only_cookies=Truena configuração do tempo de execução. Um invasor não pode definir o valor de um cookie no contexto de outro domínio. A fixação da sessão depende do envio do valor do cookie como um GET ou POST.

torre
fonte