dark icon indicating copy to clipboard operation
dark copied to clipboard

Diagnose potential JWT::verifyAndExtract regression

Open StachuDotNet opened this issue 3 years ago • 3 comments

@xtopherbrandt has reported a potential regression in JWT-decoding - the regression potentially being between the old OCaml backend and the new F# backend. The report:

Something has broken in decodeJWT. Code that was working a few months ago is now throwing an ArgumentNullException. ... image

JWT:

eyJhbGciOiJSUzI1NiIsImtpZCI6ImIxYTgyNTllYjA3NjYwZWYyMzc4MWM4NWI3ODQ5YmZhMGExYzgwNmMiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJodHRwczovL2FjY291bnRzLmdvb2dsZS5jb20iLCJuYmYiOjE2NTI5MDIyNzgsImF1ZCI6Ijg4MzgyNjY2NjExNS1xdTN1a3BnYW5uaGszaWFjdGJoanZ2amRjN3B0aGIxZi5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsInN1YiI6IjEwNDgyMTk4NTE1MjYxNjM5NjczNCIsImVtYWlsIjoieHRvcGhlci5icmFuZHRAZ21haWwuY29tIiwiZW1haWxfdmVyaWZpZWQiOnRydWUsImF6cCI6Ijg4MzgyNjY2NjExNS1xdTN1a3BnYW5uaGszaWFjdGJoanZ2amRjN3B0aGIxZi5hcHBzLmdvb2dsZXVzZXJjb250ZW50LmNvbSIsImlhdCI6MTY1MjkwMjU3OCwiZXhwIjoxNjUyOTA2MTc4LCJqdGkiOiI2NzFhZTkwYWVjMzRkMGE1N2NjMmViNGFmYThmNDZhOGQwNTQxOTc5In0.uSvFD7P-BRYp3UmeIHYhR6nK5TGkZLubl8_bCBvm7NOzN54t757dBXGGrOcwyW-w30C5hNfxiSqm8rf21Jz25RkSXEh_aZEdnXSt8C-csySgEKkHUsIfee-McVecbaiF5HuwBvCIVoQo_F_IDQNHLJvblrQn4_OoIWV7c1xIpuBFMULNRRqYSJGYEqWIs19lEymxUNmVsp0WA-MHO-DD3FjDsSEFwIa7I0MzmiaP1uhcZEVpl8g8hKOrBxcOnjd-URRCq2aYnDk-O154-754B2OD6LgVT4k1ZILxZoKa9hoMlQh69icZMYyjDjyqLheL76pTiocrNH9QR5XmQ_1w

@xtopherbrandt could you please confirm what function you're calling? Is it JWT::verifyAndExtract? If so, v0 or v1? Could you point to the place in your code (via a URL at the relevant handler) where this is happening?

StachuDotNet avatar Sep 14 '22 17:09 StachuDotNet

I've briefly started on investigation here, assuming this is with JWT::verifyAndExtract_v1. Note: while I have the JWT, I don't have the cert/key to actually verify the signature against.

The backend code for this fn is here and here.

Since I don't have the key/cert, I've commented out the lines actually verifying the signature, leaving:

let verifyAndExtractV1 (key : RSA) (token : string) : Result<string * string, string> =
  match String.split "." token with
  | [ header; payload; signature ] ->
    //do the minimum of parsing and decoding before verifying signature.
    //c.f. "cryptographic doom principle".
    try
      let signature = signature |> Base64.fromUrlEncoded |> Base64.decodeOpt
      match signature with
      | None -> Error "Unable to base64-decode signature"
      | Some signature ->
        let hash =
          (header + "." + payload) |> UTF8.toBytes |> SHA256.Create().ComputeHash
        let rsaDeformatter = RSAPKCS1SignatureDeformatter key
        rsaDeformatter.SetHashAlgorithm "SHA256"

        //if rsaDeformatter.VerifySignature(hash, signature) then
        let header = header |> Base64.fromUrlEncoded |> Base64.decodeOpt |> Option.bind UTF8.ofBytesOpt
        let payload = payload |> Base64.fromUrlEncoded |> Base64.decodeOpt |> Option.bind UTF8.ofBytesOpt

        match (header, payload) with
        | Some header, Some payload -> Ok(header, payload)
        | Some _, None -> Error "Unable to base64-decode header"
        | _ -> Error "Unable to base64-decode payload"
        // else
        //   Error "Unable to verify signature"

    with
    | e -> Error e.Message
  | _ -> Error "Invalid token format"

When I do this, I see that I'm returned the same JSON that jwt.io shows me for the given JWT - expected result.

In other words: removing the bit of our code here where we verify the signature, results in the JWT being decoded successfully. Is there any chance that you're referencing a different key? Or maybe referencing another function (not JWT::verifyAndDecode_v1)/

StachuDotNet avatar Sep 14 '22 17:09 StachuDotNet

Ahh. Sorry. The problem is in my app. The JWT sent by my client has a kid that isn't in the current Google public key. Some kind of synchronization problem.

However, that ArgumentNullException did show up again as I was tracing. Here's a link to the function and trace. I can't easily reproduce it. I didn't appear right away, it took a while so I think it maybe a related to a timeout.

xtopherbrandt avatar Sep 14 '22 17:09 xtopherbrandt

Yeah, this happens during some sort of GC process in the dotnet/webassembly runtime. We just need to make sure this error is not displayed, as I don't think it has any effect on anything.

pbiggar avatar Sep 15 '22 14:09 pbiggar

This was fixed last month

pbiggar avatar Oct 18 '22 03:10 pbiggar