Usando o JWT como sessão do PHP para resolver problemas de segurança e escalabilidade
Quando desenvolvemos nossas aplicações em PHP e queremos garantir que os dados persistam entre diferentes requisições do mesmo usuário nós, frequentemente, utilizamos do Session (session_start, $_SESSION, etc). Por exemplo, um dado que normalmente guardamos é o ID do usuário que efetuou o login — se não existe ID, não está logado, se existe ID, está logado e sabemos quem efetuou o login.
Como funciona a sessão?
A Session do PHP é uma forma conveniente e (relativamente) segura para o programador armazenar e recuperar essas informações entre diversas requisições do usuário. Quando criamos uma nova sessão em PHP, ele automaticamente faz as seguintes coisas:
- Aloca no servidor um espaço para armazenar os dados
- Devolve para quem solicitou a página a “chave” desse espaço como o Cookie “PHPSESSID”
Então, o browser, nas próximas requisições passa para o servidor esse cookie. O PHP de posse dessa chave recupera os dados armazenados e disponibiliza para o servidor.
Problemas na utilização da Sessão Padrão
Apesar de ser conveniente e fácil utilizar a Sessão Padrão do PHP baseado cookie ele apresenta alguns problemas:
Problemas de Escalablidade
Se precisarmos adicionar mais servidores para suprir uma demanda alta de acesso a utilização da sessão se torna um problema. Isso por que ela está originalmente armazenada no próprio servidor e se um usuário ora vai para um servidor ora para outro, pode ser que ele não consiga ler a sessão ou ter dados armazenados em duplicidade. Existem formas de contornar esse problema como:
- afinidade de sessão
- centralizar a sessão (memcached, redis, database, etc)
Mas ambas as soluções adicionam mais uma complexidade na configuração da infraestrutura e manutenção dos pontos de falha adicionados.
Problemas de Segurança
Esse é o mais grave. Como o Cookie é armazenado no lado do cliente significa que ele pode ser lido bem como alterado pelo cliente. Dessa forma, se o cliente quiser deliberadamente alterar o valor do seu Session ID local, eventualmente ele pode ter acesso a alguma sessão válida do seu servidor e com isso “herdar” todas as permissões do usuário. Se por acaso conseguir a sessão do administrador ele pode, literalmente, fazer tudo no sistema.
Como funciona o JWT e como pode resolver esses problemas?
Para entender o que é o Json Web Token ou simplesmente JWT precisamos entender o que é um Token.
Segundo o paper apresentado no evento FOAF-GALWAY temos [1]:
O conceito por trás de uma autenticação baseada em token é simples. Permite que os usuários entrem o login e senha de forma a obter um token que permite que eles tenha acesso a um recurso específico sem ter que utilizar o seu usuário e senha novamente. Uma vez que o seu token foi obtido, o usuário pode fornecer o token ao servidor remoto o qual proverá acesso ao recurso específico por um período de tempo.
Um Token JWT (note que Token JWT é redundante) tem os seguintes elementos:
- Mensagem — A mensagem que o servidor devolveu ao cliente.
- Payload — contém informações sobre origem, data, validade, etc.
- Assinatura — A assinatura é gerada com base em uma chave secreta e tem o objetivo de garantir a autenticidade da mensagem.
O Fluxo é basicamente o seguinte:
Com isso não precisamos armazenar os dados em nenhum servidor, simplesmente passamos para o cliente e esperamos que ele a devolva. Se a mensagem chegar de volta intacta então o acesso está garantido.
Como o Token pode ter a sua origem, validade e integridade validados através da chave secreta, podemos confiar no Token.
Usando o JWT como “replacement” da sessão do PHP
Existem várias formas de se utilizar o JWT em sua aplicação. Entretanto para sistemas legados isso passa a ser um problema pois nem sempre é fácil mexer no seu código fonte sem um grande refactoring.
Como a sessão é largamente utilizada, um bom “entrypoint” para usar o JWT seria justamente fazer a substituição da sessão padrão do PHP pela sessão que usa JWT. E utilizar o Token ao invés do Session do PHP original resolve o problema de segurança e o problema de escalabidade relatado anteriormente.
Pensando nisso, desenvolvi uma biblioteca opensource em PHP chamada “jwt-session” (github) que faz exatamente isso.
Basta adicionar ao seu projeto com o Composer:
composer require "byjg/jwt-session"
E adicionar as seguintes linhas no inicio do seu projeto:
<?php
$handler = new \ByJG\Session\JwtSession('your.domain.com', 'your super secret key');
$handler->replaceSessionHandler(true);
Pronto!! As suas sessões agora estão baseadas no JWT.
Informação muito importante: Apesar do JWT ser seguro e garantir a origem, autenticidade e validade, ele não impede que a mensagem seja lida por qualquer pessoa. Assim, não armazene na sessão dados confidenciais, pois qualquer pessoa poderá le-los.