Los tokens web JSON (JWT, que se pronuncian “jots”) son un medio compacto y altamente portátil para intercambiar información de identidad. La especificación JWT ha sido una base importante de OpenID Connect , proporcionando un token de inicio de sesión único para el ecosistema OAuth 2.0. Los JWT también se pueden utilizar como credenciales de autenticación por derecho propio y son una mejor manera de controlar el acceso a las API basadas en web que las claves API tradicionales.
NGINX Plus R10 y versiones posteriores pueden validar JWT directamente. En esta publicación de blog describimos cómo usar NGINX Plus como una puerta de enlace de API, proporcionando una interfaz a un punto final de API y usando JWT para autenticar aplicações cliente.
La compatibilidad nativa con JWT solo está disponible en NGINX Plus, no en NGINX Open Source.
Editor : esta publicación de blog se actualizó en diciembre de 2021 para utilizar la directiva auth_jwt_require
introducida en NGINX Plus R25 . Para obtener una discusión detallada de la directiva, consulte Reglas de validación de JWT personalizadas en el blog que anuncia NGINX Plus R25 .
NGINX Plus R15 y versiones posteriores también pueden controlar el “Flujo de código de autorización” en OpenID Connect 1.0, lo que permite la integración con la mayoría de los principales proveedores de identidad. Para obtener más detalles, consulte Anuncio de NGINX Plus R15<.htmlspan> .
Los JWT tienen tres partes: un encabezado, una carga útil y una firma. En transmisión se ven así: Hemos agregado saltos de línea para facilitar la lectura (el JWT real es una sola cadena) y codificación de colores para distinguir las tres partes:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 .ewogICAgInN1YiI6ICJsYzEiLAogICAgImVtYWlsIjogImxpYW0uY3JpbGx5QG5naW54LmNvbSIsCn0 = .VGYHWPterIaLjRi0LywgN3jnDUQbSsFptUw99g2slfc
Como se muestra, un punto ( .
) separa el encabezado, la carga útil y la firma. El encabezado y la carga útil son objetos JSON codificados en Base64 . La firma se cifra utilizando el algoritmo especificado por el encabezado alg
, que podemos ver cuando decodificamos nuestro JWT de muestra:
Codificado | Descifrado | |
---|---|---|
Encabezamiento | eyJhbGciOiJIUzI1NiIsInR5cCI6Ik |
{ |
Carga útil | ewogICAgInN1YiI6ICJsYzEiLAogICAgImVtYWlsIjogImxpYW0uY3JpbGx5QG5naW54LmNvbSIsCn0= |
{ |
El estándar JWT define varios algoritmos de firma. El valor HS256
en nuestro ejemplo se refiere a HMAC SHA‑256, que usamos para todos los JWT de muestra en esta publicación del blog. NGINX Plus admite los algoritmos de firma HS xxx
, RS xxx
y ES xxx
que están definidos en el estándar . La capacidad de firmar criptográficamente JWT los hace ideales para su uso como credenciales de autenticación.
Una forma común de autenticar un cliente de API (el cliente de software remoto que solicita recursos de API) es a través de un secreto compartido, generalmente denominado clave de API . Una clave API tradicional es esencialmente una contraseña larga y compleja que el cliente envía como un encabezado HTTP adicional en cada solicitud. El punto final de API otorga acceso al recurso solicitado si la clave de API proporcionada está en la lista de claves válidas. Generalmente, el punto final de API no valida las claves de API por sí mismo; en su lugar, una puerta de enlace de API maneja el proceso de autenticación y dirige cada solicitud al punto final apropiado. Además de la descarga computacional, esto proporciona los beneficios que vienen con un proxy inverso, como alta disponibilidad y equilibrio de carga en varios puntos finales de API.
Es común aplicar diferentes controles de acceso y políticas a diferentes clientes API. Con las claves API tradicionales, esto requiere una búsqueda para hacer coincidir la clave API con un conjunto de atributos. Realizar esta búsqueda en cada una de las solicitudes tiene un impacto comprensible en la latencia general del sistema. Con JWT, estos atributos están integrados, lo que elimina la necesidad de una búsqueda separada.
El uso de JWT como clave API proporciona una alternativa de alto rendimiento a las claves API tradicionales, combinando la tecnología de autenticación de mejores prácticas con un esquema basado en estándares para intercambiar atributos de identidad.
La configuración de NGINX Plus para validar JWT es muy simple.
servidor_de_api_de_subida {
servidor_10.0.0.1;
servidor_10.0.0.2;
}
servidor {
escuchar_80;
ubicación/productos/ {
auth_jwt "API de productos";
auth_jwt_key_file conf/api_secret.jwk;
contraseña_de_proxy http://servidor_de_api;
}
}
Lo primero que hacemos es especificar las direcciones de los servidores que alojan el endpoint de la API, en el bloque upstream
. El bloque de ubicación
especifica que cualquier solicitud a URL que comiencen con /productos/ debe ser autenticada. La directiva auth_jwt
define el ámbito de autenticación que se devolverá (junto con un401
Código de estado) si la autenticación no es exitosa.
La directiva auth_jwt_key_file
le dice a NGINX Plus cómo validar el elemento de firma del JWT. En este ejemplo, utilizamos el algoritmo HMAC SHA‑256 para firmar JWT y, por lo tanto, necesitamos crear una clave web JSON en conf/api_secret.jwk para contener la clave simétrica utilizada para firmar. El archivo debe seguir el formato descrito en la especificación de clave web JSON ; nuestro ejemplo se ve así:
{"keys":
[{
"k":"ZmFudGFzdGljand0",
"kty":"oct",
"kid":"0001"
}]
}
La clave simétrica se define en el campo k
y aquí está el valor codificado en Base64URL de la cadena de caracteres de texto simple fantasyjwt
. Obtuvimos el valor codificado ejecutando este comando:
$ echo -n fantásticojwt | base64 | tr '+/' '-_' | tr -d '='
El campo kty
define el tipo de clave como una clave simétrica (secuencia de octetos). Finalmente, el campo niño
(ID de clave) define un número de serie para esta clave web JSON, aquí0001
, lo que nos permite admitir múltiples claves en el mismo archivo (nombrado por la directiva auth_jwt_key_file
) y administrar el ciclo de vida de esas claves y los JWT firmados con ellas.
Ahora estamos listos para emitir JWT a nuestros clientes API.
Como cliente de API de muestra, utilizaremos una aplicação de “sistema de cotizaciones” y crearemos un JWT para el cliente de API. Primero definimos el encabezado JWT:
{
"typ":"JWT",
"alg":"HS256",
"kid":"0001"
}
El campo typ
define el tipo como JSON Web Token, el campo alg
especifica que el JWT está firmado con el algoritmo HMAC SHA256 y el campo kid
especifica que el JWT está firmado con la clave web JSON con ese número de serie.
A continuación definimos la carga útil JWT:
{
"name":"Sistema de cotizaciones",
"sub":"cotizaciones",
"iss":"Mi API Gateway"
}
El campo sub
(asunto) es nuestro identificador único para el valor completo en el campo de nombre
. El campo iss
describe el emisor del JWT, lo cual es útil si su puerta de enlace API también acepta JWT de emisores externos o de un sistema de gestión de identidad centralizado.
Ahora que tenemos todo lo necesario para crear el JWT, seguimos estos pasos para codificarlo y firmarlo correctamente. Los comandos y valores codificados aparecen en varias líneas solo para facilitar la lectura; cada uno de ellos se escribe o aparece en una sola línea.
Aplane por separado y codifique en Base64URL el encabezado y la carga útil.
$ echo -n '{"typ":"JWT","alg":"HS256","kid":"0001"}' | base64 | tr '+/' '-_' | tr -d '=' eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ $ echo -n '{"name":"Sistema de cotizaciones","sub":"cotizaciones","iss":"Mi puerta de enlace API"}' | base64 | tr '+/' '-_' | tr -d '=' eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsImlzcyI6Ik 15IEFQSSBHYXRld2F5In0
Concatene el encabezado codificado y la carga útil con un punto (.) y asigne el resultado a la variable HEADER_PAYLOAD
.
$ CARGA DE ENCABEZADO=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ.eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsIm lzcyI6Ik15IEFQSSBHYXRld2F5In0
Firme el encabezado y la carga útil con nuestra clave simétrica y codifique la firma en Base64URL.
$ echo -n $CARGA_PÚBLICA_DE_ENCABEZADO | openssl dgst -binary -sha256 -hmac fantásticojwt | base64 | tr '+/' '-_' | tr -d '=' ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I
Añade la firma codificada al encabezado y a la carga útil.
$ echo $HEADER_PAYLOAD.ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I > comillas.jwt
Pruebe realizando una solicitud autenticada a la puerta de enlace API (en este ejemplo, la puerta de enlace se ejecuta en el host local).
$ curl -H "Autorización: Portador `cat quotes.jwt`" http://localhost/products/widget1
El comando curl
en el paso 5 envía el JWT a NGINX Plus en forma de un Bearer Token , que es lo que NGINX Plus espera de manera predeterminada. NGINX Plus también puede obtener el JWT de una cookie o un parámetro de cadena de consulta; para configurarlo, incluya el parámetro token=
en la directiva auth_jwt
. Por ejemplo, con la siguiente configuración NGINX Plus puede validar el JWT enviado con este comando curl
:
$ curl http://localhost/products/widget1?apijwt=`citas de gato.jwt`
servidor { escuchar 80; ubicación /productos/ { auth_jwt "API de productos" token=$arg_apijwt ; archivo_clave_auth_jwt conf/api_secret.jwk; contraseña_proxy http://servidor_api; } }
Una vez que haya configurado NGINX Plus y haya generado y verificado un JWT como se muestra arriba, estará listo para enviar el JWT al desarrollador del cliente API y acordar el mecanismo que se utilizará para enviar el JWT con cada solicitud API.
Una de las principales ventajas de los JWT como credenciales de autenticación es que transmiten “reclamos”, que representan entidades asociadas con el JWT y su carga útil (su emisor, el usuario al que se emitió y el destinatario previsto, por ejemplo). Después de validar el JWT, NGINX Plus tiene acceso a todos los campos presentes en el encabezado y la carga útil como variables. Se accede a ellos anteponiendo $jwt_header_
o $jwt_claim_
al campo deseado (por ejemplo, $jwt_claim_sub
para el reclamo secundario
). Esto significa que podemos enviar muy fácilmente la información contenida en el JWT al punto final de la API sin necesidad de implementar el procesamiento de JWT en la API misma.
Este ejemplo de configuración muestra algunas de las capacidades avanzadas.
formato_registro jwt '$dirección_remota - $usuario_remoto [$tiempo_local] "$solicitud" ' '$estado $bytes_cuerpo_enviados "$referencia_http" "$agente_usuario_http" ' '$algoritmo_encabezado_jwt $jwt_claim_sub' ; zona_límite_solicitud $jwt_claim_sub zona=10rps_por_cliente:1m tasa=10r/s; servidor { escuchar 80; ubicación /productos/ { auth_jwt "API de productos"; archivo_clave_auth_jwt conf/api_secret.jwk; zona_límite_solicitud=10rps_por_cliente; contraseña_proxy http://servidor_api; cliente_de_API_configuración_proxy_encabezado $jwt_claim_sub; registro_acceso /var/log/nginx/access_jwt.log jwt; } }
La directiva log_format
define un nuevo formato llamado jwt
que extiende el formato de registro común con dos campos adicionales, $jwt_header_alg
y $jwt_claim_sub
. Dentro del bloque de ubicación
, utilizamos la directiva access_log
para escribir registros con los valores obtenidos del JWT validado.
En este ejemplo, también utilizamos variables basadas en reclamaciones para proporcionar una limitación de velocidad de API por cliente de API, en lugar de por dirección IP. Esto es particularmente útil cuando varios clientes API están integrados en un solo portal y no se pueden diferenciar por dirección IP. La directiva limit_req_zone
utiliza la subdemanda
JWT como clave para calcular los límites de velocidad, que luego se aplican al bloque de ubicación
mediante la inclusión de la directiva limit_req
.
Por último, proporcionamos el asunto JWT como un nuevo encabezado HTTP cuando la solicitud se envía al punto final de la API. La directiva proxy_set_header
agrega un encabezado HTTP llamado API-Client
que el punto final de la API puede consumir fácilmente. Por lo tanto, el punto final de la API no necesita implementar ninguna lógica de procesamiento JWT. Esto se vuelve cada vez más valioso a medida que aumenta el número de puntos finales de API.
De vez en cuando puede ser necesario revocar o volver a emitir el JWT de un cliente API. Al combinar un bloque de mapa
simple con la directiva auth_jwt_require
, podemos denegar el acceso a un cliente API al marcar su JWT como inválido hasta que se alcance la fecha de vencimiento del JWT (representada en el reclamo exp
), momento en el cual la entrada de mapa
para ese JWT se puede eliminar de forma segura.
En este ejemplo, configuramos la variable $jwt_status
en0
o1
de acuerdo con el valor de la sub
reclamación en el token (como se captura en la variable $jwt_claim_sub
). Luego usamos la directiva auth_jwt_require
en el bloque de ubicación
para validar (o rechazar) adicionalmente el token. Para que sea válida, la variable $jwt_status
no debe estar vacía ni ser igual a0
(cero) .
mapa $jwt_claim_sub $jwt_status { "cotizaciones" 0; "prueba" 0; predeterminado 1; } servidor { escuchar 80; ubicación /productos/ { auth_jwt "API de productos"; archivo de claves auth_jwt conf/api_secret.jwk; auth_jwt_require $jwt_status; contraseña de proxy http://servidor_api; } }
Los tokens web JSON son adecuados para proporcionar acceso autenticado a las API. Para el desarrollador del cliente API, son tan fáciles de manejar como las claves API tradicionales y proporcionan a la puerta de enlace API información de identidad que de otro modo requiere una búsqueda en una base de datos. NGINX Plus proporciona soporte para la autenticación JWT y soluciones de configuración sofisticadas basadas en la información contenida en el propio JWT. Combinado con otras capacidades de API gateway , NGINX Plus le permite ofrecer servicios basados en API con velocidad, confiabilidad, escalabilidad y seguridad.
Para probar JWT con NGINX Plus usted mismo, comience hoy su prueba gratuita de 30 días o contáctenos para analizar sus casos de uso .
"Esta publicación de blog puede hacer referencia a productos que ya no están disponibles o que ya no reciben soporte. Para obtener la información más actualizada sobre los productos y soluciones F5 NGINX disponibles, explore nuestra familia de productos NGINX . NGINX ahora es parte de F5. Todos los enlaces anteriores de NGINX.com redirigirán a contenido similar de NGINX en F5.com.