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 = 0
no seu php.ini
arquivo. 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 = 1
no seu php.ini
arquivo. 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_function
in php.ini
. Se PHP <5.3, defina-o como session.hash_function = 1
SHA1. Se PHP> = 5.3, defina-o como session.hash_function = sha256
ou session.hash_function = sha512
.
Envie um hash forte: session.hash_bits_per_character
in 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_file
e session.entropy_length
no seu php.ini
arquivo. Defina o primeiro como session.entropy_file = /dev/urandom
e 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_id
a 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();
}