¿Descodificaciones dobles para HMAC JWT que utilizan niños?

Resuelto Billy Kimble asked hace 7 meses • 0 respuestas

Estamos intentando usar ruby-jwt para codificar/decodificar JWT firmados HS256 usando kids para indicar el registro en un almacén de claves que representa la clave a usar, pero parece que necesitamos hacer 2 decodificaciones para la verificación. Una decodificación que no usa un secreto ni indica a ruby-jwt que queremos verificar el JWT solo para poder eliminarlo kid, luego una segunda decodificación que pasa el secreto que kidrepresenta y la bandera para verificar el JWT. Es una cosa menor y b64 es barato, pero ¿hacer 2 decodificaciones es realmente el enfoque que se debe adoptar para los HMAC JWT que contienen kid? ¿Es esto una limitación de la biblioteca o una falta de conocimiento sobre cómo trabajar con kidsHMAC?

Crear JWT con HS256

keys = {'31b88f20-8edd-49fe-a839-57b8f681888c' => 'mysecret'}
payload = {user_id: 99, kid: keys.first.first, exp: 5.years.from_now.to_i}
token = JWT.encode(payload, keys.first.second, 'HS256')

Verificar JWT

# Get the kid from the jwt so we can look up what secret to use to verify it by the kid
decoded_jwt = JWT.decode(token, nil, false)
kid = decoded_jwt[0]['kid']
secret = keys[kid]
# decode the jwt again with the secret, and verify it
decoded_jwt = JWT.decode(token, secret, true)
# => [{"user_id"=>99, "kid"=>"31b88f20-8edd-49fe-a839-57b8f681888c", "exp"=>1865946624}, {"alg"=>"HS256"}]

Funciona, pero intento ver si hay un enfoque mejor. ¡Gracias!

Billy Kimble avatar Feb 16 '24 21:02 Billy Kimble
Aceptado

Puede evitar la doble decodificación utilizando JSON Web Key Set (JWKS).

Para implementar este proceso usando JWKS y HMAC, puede hacerlo de la siguiente manera.

Codificación:

require 'jwt'

secret = 'mysecret'
kid = '31b88f20-8edd-49fe-a839-57b8f681888c'
algorithm = 'HS256'

# Used strings for comparison at the end 
# Used Time so it did not rely on Rails
payload = {"user_id" =>  99, "exp" => Time.now.to_i + 10000}

# Encoding pass kid as a header field 
token = JWT.encode(payload, secret, algorithm , kid: kid)
#=> "eyJraWQiOiIzMWI4OGYyMC04ZWRkLTQ5ZmUtYTgzOS01N2I4ZjY4MTg4OGMiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjo5OSwiZXhwIjoxNzA4MTE3ODIzfQ.UArtkNyPx0ri08i6D2t7sLtZ_dlqoDczP7r4ZVPtTGM"

Claves de exportación:

Dado que HMAC es una clave simétrica, el tipo de clave ( kty) debe establecerse en 'oct'. Consulte RFC7518 en 6.1 , y el valor de clave ( k) debe establecerse en secreto. Consulte RFC7518 en 6.4.1.

#Create a JWK by importing the Hash
jwk = JWT::JWK.new({ kty: 'oct', k: secret, kid: kid, alg: algorithm  })

# JSON Web Key Set for advertising your signing keys
jwks_hash = JWT::JWK::Set.new(jwk).export
#=> {:keys=>[{:kty=>"oct", :kid=>"31b88f20-8edd-49fe-a839-57b8f681888c", :alg=>"HS256"}]}

# OR export private keys (Not recommended refer to: https://www.rfc-editor.org/rfc/rfc7517.html#section-9.2)

jwks_hash = JWT::JWK::Set.new(jwk).export(include_private: true)
#=> {:keys=>[{:kty=>"oct", :k=>"mysecret", :kid=>"31b88f20-8edd-49fe-a839-57b8f681888c", :alg=>"HS256"}]}

Descodificación:

# If you did not export the private secret, if you did this step can be skipped
jwks_hash[:keys].each {|k| k.merge!(k: secret)}

# Create a JWKS
jwks = JWT::JWK::Set.new(jwks_hash)

#Decoding
decoded = JWT.decode(token, nil, true, jwks: jwks)
#=> [{"user_id"=>1708119080, "exp"=>1708119627}, {"kid"=>"31b88f20-8edd-49fe-a839-57b8f681888c", "alg"=>"HS256"}]

decoded[0] == payload
#=> true
engineersmnky avatar Feb 16 '2024 18:02 engineersmnky