JSON Web Tokens (JWTs, pronunciado “jots”) são um meio compacto e altamente portátil de troca de informações de identidade. A especificação JWT tem sido um importante suporte do OpenID Connect , fornecendo um token de logon único para o ecossistema OAuth 2.0. Os JWTs também podem ser usados como credenciais de autenticação por si só e são uma maneira melhor de controlar o acesso a APIs baseadas na Web do que as chaves de API tradicionais.
O NGINX Plus R10 e versões posteriores podem validar JWTs diretamente. Nesta postagem do blog, descrevemos como usar o NGINX Plus como um gateway de API, fornecendo um front-end para um endpoint de API e usando JWTs para autenticar aplicativos cliente.
O suporte nativo ao JWT está disponível apenas no NGINX Plus, não no NGINX Open Source.
Editor – Esta postagem do blog foi atualizada em dezembro de 2021 para usar a diretiva auth_jwt_require
introduzida no NGINX Plus R25 . Para uma discussão detalhada da diretiva, consulte Regras de validação de JWT personalizadas no blog que anuncia o NGINX Plus R25 .
O NGINX Plus R15 e versões posteriores também podem controlar o “Fluxo de Código de Autorização” no OpenID Connect 1.0, o que permite a integração com a maioria dos principais provedores de identidade. Para obter detalhes, consulte Anunciando o NGINX Plus R15<.htmlspan> .
Os JWTs têm três partes: um cabeçalho, uma carga útil e uma assinatura. Na transmissão, eles se parecem com o seguinte. Adicionamos quebras de linha para facilitar a leitura (o JWT real é uma única string) e codificação de cores para distinguir as três partes:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 . ewogICAgInN1YiI6ICJsYzEiLAogICAgImVtYWlsIjogImxpYW0uY3JpbGx5QG5naW54LmNvbSIsCn0= . VGYHWPterIaLjRi0LywgN3jnDUQbSsFptUw99g2slfc
Conforme mostrado, um ponto ( .
) separa o cabeçalho, a carga útil e a assinatura. O cabeçalho e a carga útil são objetos JSON codificados em Base64 . A assinatura é criptografada usando o algoritmo especificado pelo cabeçalho alg
, que podemos ver quando decodificamos nosso JWT de exemplo:
Codificado | Decodificado | |
---|---|---|
Cabeçalho | eyJhbGciOiJIUzI1NiIsInR5cCI6Ik |
{ |
Carga útil | ewogICAgInN1YiI6ICJsYzEiLAogICAgImVtYWlsIjogImxpYW0uY3JpbGx5QG5naW54LmNvbSIsCn0= |
{ |
O padrão JWT define vários algoritmos de assinatura. O valor HS256
em nosso exemplo se refere ao HMAC SHA‑256, que estamos usando para todos os JWTs de exemplo nesta postagem do blog. O NGINX Plus suporta os algoritmos de assinatura HS xxx
, RS xxx
e ES xxx
definidos no padrão . A capacidade de assinar criptograficamente JWTs os torna ideais para uso como credenciais de autenticação.
Uma maneira comum de autenticar um cliente de API (o cliente de software remoto que solicita recursos de API) é por meio de um segredo compartilhado, geralmente chamado de chave de API . Uma chave de API tradicional é essencialmente uma senha longa e complexa que o cliente envia como um cabeçalho HTTP adicional em cada solicitação. O ponto de extremidade da API concede acesso ao recurso solicitado se a chave de API fornecida estiver na lista de chaves válidas. Geralmente, o ponto de extremidade da API não valida as chaves da API por si só; em vez disso, um gateway de API lida com o processo de autenticação e encaminha cada solicitação para o ponto de extremidade apropriado. Além do descarregamento computacional, isso fornece os benefícios que vêm com um proxy reverso, como alta disponibilidade e balanceamento de carga para vários pontos de extremidade de API.
É comum aplicar diferentes controles de acesso e políticas a diferentes clientes de API. Com chaves de API tradicionais, isso requer uma pesquisa para corresponder a chave de API com um conjunto de atributos. Executar essa pesquisa em cada solicitação tem um impacto compreensível na latência geral do sistema. Com o JWT, esses atributos são incorporados, eliminando a necessidade de uma pesquisa separada.
Usar o JWT como chave de API fornece uma alternativa de alto desempenho às chaves de API tradicionais, combinando a tecnologia de autenticação de melhores práticas com um esquema baseado em padrões para troca de atributos de identidade.
A configuração do NGINX Plus para validar JWTs é muito simples.
upstream api_server {
servidor 10.0.0.1;
servidor 10.0.0.2;
}
servidor {
listen 80;
localização /produtos/ {
auth_jwt "API de produtos";
auth_jwt_key_file conf/api_secret.jwk;
proxy_pass http://api_server;
}
}
A primeira coisa que fazemos é especificar os endereços dos servidores que hospedam o ponto de extremidade da API, no bloco upstream
. O bloco de localização
especifica que quaisquer solicitações para URLs que começam com /products/ devem ser autenticadas. A diretiva auth_jwt
define o domínio de autenticação que será retornado (junto com um401
código de status) se a autenticação não for bem-sucedida.
A diretiva auth_jwt_key_file
informa ao NGINX Plus como validar o elemento de assinatura do JWT. Neste exemplo, estamos usando o algoritmo HMAC SHA‑256 para assinar JWTs e, portanto, precisamos criar uma JSON Web Key em conf/api_secret.jwk para conter a chave simétrica usada para assinatura. O arquivo deve seguir o formato descrito pela especificação JSON Web Key ; nosso exemplo se parece com isto:
{"chaves":
[{
"k":"ZmFudGFzdGljand0",
"kty":"outubro",
"criança":"0001"
}]
}
A chave simétrica é definida no campo k
e aqui está o valor codificado em Base64URL da sequência de caracteres de texto simples fantasticjwt
. Obtivemos o valor codificado executando este comando:
$ echo -n fantasticjwt | base64 | tr '+/' '-_' | tr -d '='
O campo kty
define o tipo de chave como uma chave simétrica (sequência de octetos). Por fim, o campo kid
(Key ID) define um número de série para esta JSON Web Key, aqui0001
, o que nos permite oferecer suporte a várias chaves no mesmo arquivo (nomeadas pela diretiva auth_jwt_key_file
) e gerenciar o ciclo de vida dessas chaves e dos JWTs assinados com elas.
Agora estamos prontos para emitir JWTs para nossos clientes de API.
Como um cliente de API de exemplo, usaremos um aplicativo de “sistema de cotação” e criaremos um JWT para o cliente de API. Primeiro definimos o cabeçalho JWT:
{
"tipo":"JWT",
"alg":"HS256",
"criança":"0001"
}
O campo typ
define o tipo como JSON Web Token, o campo alg
especifica que o JWT é assinado com o algoritmo HMAC SHA256 e o campo kid
especifica que o JWT é assinado com a JSON Web Key com esse número de série.
Em seguida, definimos a carga útil do JWT:
{
"name":"Sistema de cotação",
"sub":"quotes",
"iss":"Meu API Gateway"
}
O campo sub
(assunto) é nosso identificador exclusivo para o valor completo no campo de nome
. O campo iss
descreve o emissor do JWT, o que é útil se o seu gateway de API também aceitar JWTs de emissores terceirizados ou de um sistema de gerenciamento de identidade centralizado.
Agora que temos tudo o que precisamos para criar o JWT, seguimos estes passos para codificá-lo e assiná-lo corretamente. Comandos e valores codificados aparecem em várias linhas apenas para facilitar a leitura; cada um deles é digitado ou aparece em uma única linha.
Achate separadamente e codifique em Base64URL o cabeçalho e a carga útil.
$ echo -n '{"typ":"JWT","alg":"HS256","kid":"0001"}' | base64 | tr '+/' '-_' | tr -d '=' eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ $ echo -n '{"name":"Sistema de cotações","sub":"quotes","iss":"Meu gateway de API"}' | base64 | tr '+/' '-_' | tr -d '=' eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsImlzcyI6Ik 15IEFQSSBHYXRld2F5In0
Concatene o cabeçalho codificado e a carga útil com um ponto (.) e atribua o resultado à variável HEADER_PAYLOAD
.
$ HEADER_PAYLOAD=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ.eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsIm lzcyI6Ik15IEFQSSBHYXRld2F5In0
Assine o cabeçalho e a carga útil com nossa chave simétrica e codifique a assinatura em Base64URL.
$ echo -n $HEADER_PAYLOAD | openssl dgst -binário -sha256 -hmac fantasticjwt | base64 | tr '+/' '-_' | tr -d '=' ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I
Anexe a assinatura codificada ao cabeçalho e à carga útil.
$ echo $HEADER_PAYLOAD.ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I > citações.jwt
Teste fazendo uma solicitação autenticada ao gateway da API (neste exemplo, o gateway está sendo executado no host local).
$ curl -H "Autorização: Portador `cat quotes.jwt`" http://localhost/products/widget1
O comando curl
na Etapa 5 envia o JWT para o NGINX Plus na forma de um Bearer Token , que é o que o NGINX Plus espera por padrão. O NGINX Plus também pode obter o JWT de um cookie ou parâmetro de string de consulta; para configurar isso, inclua o parâmetro token=
na diretiva auth_jwt
. Por exemplo, com a seguinte configuração, o NGINX Plus pode validar o JWT enviado com este comando curl
:
$ curl http://localhost/products/widget1?apijwt=`citações de gato.jwt`
servidor { ouvir 80; localização /produtos/ { auth_jwt "API de produtos" token=$arg_apijwt ; auth_jwt_key_file conf/api_secret.jwk; proxy_pass http://api_server; } }
Depois de configurar o NGINX Plus e gerar e verificar um JWT conforme mostrado acima, você estará pronto para enviar o JWT ao desenvolvedor do cliente da API e concordar com o mecanismo que será usado para enviar o JWT com cada solicitação de API.
Uma das principais vantagens dos JWTs como credenciais de autenticação é que eles transmitem “declarações”, que representam entidades associadas ao JWT e sua carga útil (seu emissor, o usuário para quem foi emitido e o destinatário pretendido, por exemplo). Após validar o JWT, o NGINX Plus tem acesso a todos os campos presentes no cabeçalho e na carga útil como variáveis. Eles são acessados prefixando $jwt_header_
ou $jwt_claim_
ao campo desejado (por exemplo, $jwt_claim_sub
para a sub
reivindicação). Isso significa que podemos facilmente enviar por proxy as informações contidas no JWT para o ponto de extremidade da API sem precisar implementar o processamento do JWT na própria API.
Este exemplo de configuração mostra alguns dos recursos avançados.
log_format jwt '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" "$http_user_agent" ' '$jwt_header_alg $jwt_claim_sub' ; limit_req_zone $jwt_claim_sub zona=10rps_por_cliente:1m taxa=10r/s; servidor { escutar 80; localização /produtos/ { auth_jwt "API de produtos"; auth_jwt_key_file conf/api_secret.jwk; limit_req zona=10rps_por_cliente; proxy_pass http://api_server; proxy_set_header API-Client $jwt_claim_sub; log_de_acesso /var/log/nginx/access_jwt.log jwt; } }
A diretiva log_format
define um novo formato chamado jwt
que estende o formato de log comum com dois campos adicionais, $jwt_header_alg
e $jwt_claim_sub
. Dentro do bloco de localização
, usamos a diretiva access_log
para escrever logs com os valores obtidos do JWT validado.
Neste exemplo, também estamos usando variáveis baseadas em declarações para fornecer limitação de taxa de API por cliente de API, em vez de por endereço IP. Isso é particularmente útil quando vários clientes de API estão incorporados em um único portal e não podem ser diferenciados por endereço IP. A diretiva limit_req_zone
usa a subdeclaração
JWT como a chave para calcular limites de taxa, que são então aplicados ao bloco de localização
incluindo a diretiva limit_req
.
Por fim, fornecemos o assunto JWT como um novo cabeçalho HTTP quando a solicitação é enviada por proxy para o ponto de extremidade da API. A diretiva proxy_set_header
adiciona um cabeçalho HTTP chamado API‑Client
que o ponto de extremidade da API pode consumir facilmente. Portanto, o ponto de extremidade da API não precisa implementar nenhuma lógica de processamento JWT. Isso se torna cada vez mais valioso à medida que o número de endpoints de API aumenta.
De tempos em tempos, pode ser necessário revogar ou reemitir o JWT de um cliente de API. Ao combinar um bloco de mapa
simples com a diretiva auth_jwt_require
, podemos negar acesso a um cliente de API marcando seu JWT como inválido até que a data de expiração do JWT (representada na declaração exp
) seja atingida, momento em que a entrada de mapa
para esse JWT pode ser removida com segurança.
Neste exemplo, estamos definindo a variável $jwt_status
para0
ou1
de acordo com o valor da sub
reivindicação no token (conforme capturado na variável $jwt_claim_sub
). Em seguida, usamos a diretiva auth_jwt_require
no bloco de localização
para validar (ou rejeitar) adicionalmente o token. Para ser válida, a variável $jwt_status
não deve estar vazia e não deve ser igual a0
(zero) .
mapa $jwt_claim_sub $jwt_status { "quotes" 0; "teste" 0; padrão 1; } servidor { ouvir 80; localização /produtos/ { auth_jwt "API de produtos"; auth_jwt_key_file conf/api_secret.jwk; auth_jwt_require $jwt_status; proxy_pass http://api_server; } }
Os JSON Web Tokens são adequados para fornecer acesso autenticado a APIs. Para o desenvolvedor do cliente de API, elas são tão fáceis de manusear quanto as chaves de API tradicionais e fornecem ao gateway de API informações de identidade que, de outra forma, exigiriam uma consulta no banco de dados. O NGINX Plus fornece suporte para autenticação JWT e soluções de configuração sofisticadas com base nas informações contidas no próprio JWT. Combinado com outros recursos de gateway de API , o NGINX Plus permite que você forneça serviços baseados em API com velocidade, confiabilidade, escalabilidade e segurança.
Para experimentar o JWT com NGINX Plus, comece hoje mesmo seu teste gratuito de 30 dias ou entre em contato conosco para discutir seus casos de uso .
"Esta postagem do blog pode fazer referência a produtos que não estão mais disponíveis e/ou não têm mais suporte. Para obter as informações mais atualizadas sobre os produtos e soluções F5 NGINX disponíveis, explore nossa família de produtos NGINX . O NGINX agora faz parte do F5. Todos os links anteriores do NGINX.com redirecionarão para conteúdo semelhante do NGINX no F5.com."