Back-End

29 nov, 2017

Entendendo o JWT

Publicidade

Um tempo atrás tive que implementar uma API para a empresa onde estou trabalhando. Fazendo algumas pesquisas sobre qual método deveria utilizar para fazer a autenticação. Decidi por usar JSON Web Token o famoso JWT. Hoje quero contar como ele funciona e como implementá-lo sem nenhuma biblioteca, usando minha linguagem do coração, o PHP.

JSON Web Token

O JWT é um padrão aberto, documentado pelo RFC 7519. Com ele conseguimos transmitir informações, garantindo a sua autenticidade, podendo ser usado na autenticação de APIs, sistemas ou em ações mais específicas, como recuperar a senha de um usuário, por exemplo.

Um JWT consegue ser compacto e autônomo. Com isso, quero dizer que devido ao seu pequeno tamanho, podemos colocar ele dentro de URLs, como um parâmetro do POST ou dentro de um header do HTTP, e ainda assim transmiti-lo de forma rápida. E por ser autônomo, todo o conteúdo necessário para realizar a autenticação está presente nele mesmo, sem a necessidades de uma consulta no banco de dados ou algo parecido.

A estrutura do JWT

Um JWT é divido em três partes separadas por ponto ”.”: um header, um payload e uma signature. Vamos falar delas separadamente enquanto implementamos nosso token em PHP.

Header

O header basicamente consiste em dois valores: um é o tipo do token, que nesse caso é JWT, e o segundo valor é o algoritmo utilizado de hashing, como o HMAC SHA-256 ou RSA. No nosso caso, usaremos o HS256.

<?php
$header = [
   'alg' => 'HS256',
   'typ' => 'JWT'
];

$header = json_encode($header);
$header = base64_encode($header);

Criamos um vetor chamado de $header com as duas informações necessárias e codificamos ela para um JSON e depois para base64, tendo um retorno semelhante a este:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

Payload

O payload é o corpo do JWT. Aqui você colocará todas as informações que deseja armazenar. Não abuse da quantidade de informações no seu payload para não tornar o seu token um fardo para a aplicação.

$payload = [
   'iss' => 'localhost',
   'name' => 'Diogo',
   'email' => 'diogo.fragabemfica@gmail.com'
];
$payload = json_encode($payload);
$payload = base64_encode($payload);

Usamos o mesmo processo de antes. Criamos um vetor chamado de $payload, armazenamos as informações que queremos armazenar no token. Codificamos ela para JSON e depois para base64. Tendo um retorno semelhante a este:

eyJpc3MiOiJsb2NhbGhvc3QiLCJuYW1lIjoiRGlvZ28iLCJlbWFpbCI6ImRpb2dvLmZyYWdhYmVtZmljYUBnbWFpbC5jb20ifQ==

Obs: O JWT tem palavras reservadas e recomendadas para serem colocadas dentro do payload. São elas:

  • “iss” O domínio da aplicação geradora do token
  • “sub” É o assunto do token, mas é muito utilizado para guarda o ID do usuário
  • “aud” Define quem pode usar o token
  • “exp” Data para expiração do token
  • “nbf” Define uma data para qual o token não pode ser aceito antes dela
  • “iat” Data de criação do token
  • “jti” O id do token

Por se tratar de recomendações, elas não são obrigatórias, mas se não quiser usar nenhuma delas, recomendo que não sobrescreva para evitar conflito com as bibliotecas que usarem para implementar o JWT.

Signature

O signature é a nossa assinatura. Vamos pegar o nosso header e nosso payload e codificar com o algoritmo escolhido anteriormente, junto à uma chave da nossa escolha. Neste caso, usei “minha-senha” como exemplo, mas por favor, utilize uma senha mais segura no seu projeto.

$signature = hash_hmac('sha256',"$header.$payload",'minha-senha',true);
$signature = base64_encode($signature);

Se seguirmos o código acima, vamos ter algo parecido com isso.

SrcDhO9LLSNYPHl7nAWJnLXdH7QCK4oRLfFMz48zgwk=

Agora é só juntamos tudo para termos o nosso token. Lembrando que as três partes são separadas por ponto.

echo "$header.$payload.$signature";

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJsb2NhbGhvc3QiLCJuYW1lIjoiRGlvZ28iLCJlbWFpbCI6ImRpb2dvLmZyYWdhYmVtZmljYUBnbWFpbC5jb20ifQ==.SrcDhO9LLSNYPHl7nAWJnLXdH7QCK4oRLfFMz48zgwk=

Se tudo ocorrer como o esperado, teremos um token semelhante com o que está acima e um script PHP parecido com este:

<?php
$header = [
   'alg' => 'HS256',
   'typ' => 'JWT'
];
$header = json_encode($header);
$header = base64_encode($header);

$payload = [
   'iss' => 'localhost',
   'name' => 'Diogo',
   'email' => 'diogo.fragabemfica@gmail.com'
];
$payload = json_encode($payload);
$payload = base64_encode($payload);

$signature = hash_hmac('sha256',"$header.$payload",'minha-senha',true);
$signature = base64_encode($signature);

echo "$header.$payload.$signature";

Testando nosso JWT

Agora podemos testar se o nosso token está funcionando. Para isso, vamos usar o site JWT.io. Cole o seu token no campo Encoded como mostra a imagem abaixo.

Podemos ver todas as informações do token e também que o token está inválido. Para resolver isso, basta ir até a sessão VERIFY SIGNATURE e colocar a chave usada na hora da assinatura. No nosso caso a chave é “minha-senha”.

Agora podemos ver que o nosso token está marcado como válido e está funcionando perfeitamente.

Validando o Token

Agora temos o nosso token prontinho e pronto para ser usado. Precisamos saber como fazer para que a nossa aplicação PHP consiga validá-lo. Vamos imaginar que você tem uma página PHP esperando que você passe o token pela url. Pensando nisso, precisamos criar o seguinte código:

<?php

$token = $_GET['token'];

$part = explode(".",$token);
$header = $part[0];
$payload = $part[1];
$signature = $part[2];

Vamos capturar o token usando o $_GET e separá-lo novamente com o explode, lembrando que usamos o ponto para unir as partes. Assim podemos ter novamente o $header, $payload e o $signature.

Para fazer a validação, precisamos pegar o $header e o $payload e a mesma chave que utilizamos anteriormente e passar pelo mesmo processo de assinatura.

$valid = hash_hmac('sha256',"$header.$payload",'minha-senha',true);
$valid = base64_encode($valid);

if($signature == $valid){
   echo "valid";
} else{
   echo 'invalid';
}

Se a assinatura gerada for igual a anterior, então garantimos que o token não sofreu nenhuma alteração e as informações contidas nele estão íntegras. Se qualquer alteração for feita no token, tanto no seu header quanto no payload ou na assinatura, a nova assinatura não seria igual, e para qualquer um que queira criar um token assinado, precisará da palavra chave.

Com essa validação, o usuário agora pode acessar o sistema, recuperar sua senha ou o que ele mais desejar e tiver acesso para fazer.

Se tudo estiver como o esperado, seu arquivo para validar o token ficará assim:

<?php

$token = $_GET['token'];

$part = explode(".",$token);
$header = $part[0];
$payload = $part[1];
$signature = $part[2];

$valid = hash_hmac('sha256',"$header.$payload",'minha-senha',true);
$valid = base64_encode($valid);

if($signature == $valid){
   echo "valid";
}else{
   echo 'invalid';
}

É muito importante destacar que o JWT garante a autenticidade e não confidencialidade das informações presentes nele. Então jamais deve-se colocar informações sensíveis como senha de usuário dentro do payload, pois, como visto no site JWT.io, essas informações são de fácil acesso. Use o JWT com sabedoria.

Espero que esse conhecimento tenha ajudado a entender como é o funcionamento básico do JWT e te incentive a pesquisar mais sobre assunto.

Referências