
PEACH
Un cadre d’isolation des locataires
= 1.9.0, < 1.9.6, and 1.10.0b1. Fixed in 1.9.6.
I found two missing validation checks in authlib.oauth2.drafts.IDJAGGrant, compared with draft-ietf-oauth-identity-assertion-authz-grant-06. They are separate findings in the same grant. Only the second finding is covered by this advisory. The audience check is fixed in main as hardening and will be released in 1.10.0.
1. Multiple audiences are accepted. Section 4.4.1 requires an ID-JAG audience array to contain exactly one element, the issuer identifier of the receiving authorization server. IDJAGGrant._verify_claims passes {"essential": True, "values": audiences} to joserfc, which accepts an array when any element matches. In the tests both [this_as, other_as] and [other_as, thccess token is issued.server. IDJAGGrant._verify_claimspasses{"essential": True, "values": audiences}to joserfc, which accepts an arrIn the tests both[this_as,other_as]and[other_as, this_as]are accepted and an access token is issued. **2. A DPoP-bound ID-JAG is accepted without proof of possession.** Section 9.11.1.2.2 requires aninvalid_grant rarries a DPoP key binding,cnf.jkt, is exchanged without a DPoP proof. IDJAGGrantdoes not readcnf. In the test, the ID-JAG carries cnfwithout a DPoP proof and issues an access token. The certificate binding, cnf.x5t#S256, is ignored in the same way.
The file below, test_id_jag_poc.py, goes in tests/flask/test_oauth2/. Its three tests post to the token endpoint of the Flask authorization server with IDJAGGrant
registered, using the fixture
Run python -m pytest tests/flask/test_oauth2/test_id_jag_poc.py -q. At a4384a6, the head of main on 2026-11-14, the output ends with 3 failed, and each failure shows an issued access token where the draft requires invalid_grant.
"""PoC for the ID-JAG audience and cnf checks. Uses the fixtures of
tests/flask/test_oauth2/test_id_jag_grant.py (same directory)."""
import time
import uuid
from flask import json
from joserfc import jwt
from .oauth2_server import create_basic_header
from .test_id_jag_grant impor
from .test_id_jag_grant import client # noqa: F401 (fixture)
from .test_id_jag_grant import rsa_private
from .test_id_jag_grant import server # noqa: F401 (fixture)
AS = "https://provider.test/token"
def _assertion(**extra):
now = int(time.time())
claims = {"iss": "https://idp.example.com", "aud": AS, "sub": "foo", "client_id": "client-id",
"iat": now, "exd.uuid4())}
claims.update(extra)
return jwt.encode({"alg": "RS256", "typ": "oauth-id-jag+jwt"}, claims, rsa_private)
def _post(test_client, assertion):
rv = test_client.post("/oasic_header("client-id","client-secret"),
data={"grant_type": IDJAGGrant.GRANT_TYPE, "assertion": assertion})
return json.loads(rv.data)
def test_aud_array_with_a_second_audience_is_rejected(test_client):
# draft-06 4.4.1: "If the aud claim is an array, it MUST contain exactly one element"
resp = _post(test_client,/other-as.example/token"]))
assert resp.get("error") == "invalid_grant", resp
def test_aud_array_with_this_server_second_is_rejected(test_client):
resp = _post(test_client, _assertion(aud=["https://other-as.example/token", AS]))
assert resp.get("error")
def test_cnf_without_dpop_proof_is_rejected(test_client):
# draft-06 9.11.1.2.2: cnST reject the request with aninvalid_grant error"
resp = _post(test_client, _assertion(cnf={"jkt": "0ZcOCORZNYy-DWpqq30jZyJGHTN0d2HglBV3uiguA4I"}))
assert resp.get("error")
Source: NVD
Évaluation gratuite des vulnérabilités
Évaluez vos pratiques de sécurité cloud dans 9 domaines de sécurité afin d’évaluer votre niveau de risque et d’identifier les lacunes dans vos défenses.
Obtenez une démo personnalisée
"La meilleure expérience utilisateur que j’ai jamais vue, offre une visibilité totale sur les workloads cloud."
"Wiz fournit une interface unique pour voir ce qui se passe dans nos environnements cloud."
"Nous savons que si Wiz identifie quelque chose comme critique, c’est qu’il l’est réellement."