BLOG | NGINX

Autenticação de clientes de API com JWT e NGINX Plus

NGINX-Parte-de-F5-horiz-preto-tipo-RGB
Miniatura de Liam Crilly
Liam Crilly
Publicado em 02 de dezembro de 2021

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> .

Anatomia de um JWT

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
pXVCJ9
{
"alg": "HS256",
"tipo": "JWT"
}
Carga útil ewogICAgInN1YiI6ICJsYzEiLAogICAgImVtYWlsIjogImxpYW0uY3JpbGx5QG5naW54LmNvbSIsCn0= {
"sub": "lc1",
"e-mail": "liam.crilly@nginx.com",
}

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.

JWT como uma chave de API

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.

Cliente API e autenticação JWT com uma chave API tradicional
O gateway da API valida a chave da API consultando um registro de chaves antes de passar a solicitação
para o ponto final da 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.

Cliente API e autenticação JWT com JWT e NGINX Plus
O NGINX Plus valida o JWT antes de passar a solicitação para os endpoints da API

Configurando o NGINX Plus como um gateway de API de autenticação

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.

Emitindo um JWT para 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.

  1. 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
    
  2. 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
    
  3. 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
    
  4. Anexe a assinatura codificada ao cabeçalho e à carga útil.

    $ echo $HEADER_PAYLOAD.ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I > citações.jwt
    
  5. 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.

Aproveitando as reivindicações do JWT para registro e limitação de taxa

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.

Revogando JWTs

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; } }

Resumo

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."