[Python] How do I "sign" a JWT using HMAC-SHA256 with a public key (RSA or EC) in order to...

Discussão em 'Python' iniciado por Stack, Julho 9, 2025.

  1. Stack

    Stack Membro Participativo

    I am trying to understand exactly how exactly it is possible to trigger JWT algorithm confusion, as described in https://redfoxsec.com/blog/jwt-deep-dive-into-algorithm-confusion/ - in section "How does Algorithm Confusion Occur":

    How does Algorithm Confusion Occur


    Think of 2 types of encryption algorithms:

    • HMAC(Symmetric):
      There’s only 1 key involved, AKA the secret
      The JWT will be signed and validated using the same secret key.
    • RSA(Asymmetric):
      There’s 2 keys involved. A private key, and a public key.
      The JWT will be signed using a private key, and the signature will be validated using a public key.

    A scenario where algorithm confusion is exploited can be as follows:


    1. An attacker authenticates to a web server with valid credentials.


    2. The web server generates a JWT, and signs it with an RSA private key, only known by the server.


    3. This JWT is then passed on to the attacker.


    4. The public key is exposed on an endpoint such as /.well-known/jwks.json.


    5. The attacker now:
      • Downloads the public key.
      • Tampers with the JWT.
      • Signs the JWT with the exposed public key.
      • Changes the encryption algorithm to HMAC.
      • Lastly, Sends the request across.

    6. Now, in a normal scenario, where the encryption algorithm is RSA, the server reads the signature, tries to verify it using the public key, but fails, because the public key can only verify signatures that have been generated using the private key (asymmetric encryption).


    7. However, since in this scenario the attacker had modified the encryption algorithm to HMAC (a symmetrical encryption), the server attempted and succeeded in verifying signature using its public key.


    8. This happens, because HMAC is a symmetrical encryption, and the key that is used to sign the JWT, is the same key, that will be used to validate it.


    9. And because the new tampered JWT has been signed with the exposed public key, and the algorithm in use is HMAC, the JWT gets validated.
    ​

    I am trying to understand how exactly the attacker can "sign the JWT with the exposed private key" (I want to write a unit test to check that my implementation logic is not vulnerable).

    I am trying to implement this logic in Python, using the jwcrypto library.

    However, when I try to write how it would work:

    from jwcrypto.jwt import JWT

    ec_key = load_jwk("sign-ec-p256") # This is a function that will load a jwcrypto.jwk.JWK object of type `EC` with only the public part
    jwt_object = JWT(header={"alg":"HS256", "kid": "sign-ec-p256"}, claims={"iss": "https://my-domain.com"})
    jwt_object.make_signed_token(ec_key)


    I get a stack trace in the library, because it tries to load the private part, which is not present and thus triggers an error before I can ever get a badly signed JWT.

    From what I understand, it seems to me that:

    • the described type of algorithm confusion can only occur if the key is transmitted to the API in a serialized form, and if the deserialization of the key object is only done by trusting the alg field in JWT;
    • the jwcrypto library avoids that issue completely because its API requires that key objects be already deserialized before passing them for usage in signature/verification functions;

    Is that undestanding correct? That "JWT algorithm confusion" is related to letting the JWT alg value direct the deserialization of a key represented in the program as a string?

    Continue reading...

Compartilhe esta Página