Subject: [OAUTH-WG] Reuse of "state" across different AS in draft-bradley-oauth-jwt-encoded-state-08

From: Daniel Fett <[email-masked]>
Date: Fri, 18 May 2018 15:46:05 +0200

Hi all,

Looking at draft-bradley-oauth-jwt-encoded-state-08, I see in Section 2
(https://tools.ietf.org/html/draft-bradley-oauth-jwt-encoded-state-08#section-2)
that all contents of the JWT that would bind the specific state value to
the AS are optional. This means that the following attack is possible
(we described this attack in our OAuth paper [0], Section 5.1, albeit
very briefly):

Precondition: A client that supports multiple AS (with one being an
attacker-controlled AS, let's call it A-AS).

1. An honest user tries to authorize using A-AS, the attacker receives
the value of "state" which is bound to the honest user's session with
the client.

2. A-AS aborts the authorization (e.g., by redirecting the user back to
the client's web site without any parameters).

3. The user starts a new authorization flow with an honest AS (H-AS).

Since the state JWT is not bound to the AS selected by the user, the
state value generated in (1.) will likely be valid for the login flow
started in (3.). The attacker therefore knows a valid state value and
can mount a CSRF attack (which was supposed to be prevented by using state).

Solution: Always bind state to the chosen AS, i.e., make the "as" part
of the JWT mandatory

-or- use some other mechanism on the client (if the client is stateful)
to invalidate the old state value.

If we agree that this is problematic, I think this should be changed in
draft-bradley-oauth-jwt-encoded-state and should at least briefly be
discussed in the security topics document (I will be happy to write a
section for the latter).

- Daniel

[0]
https://sec.uni-stuttgart.de/_media/publications/FettKuestersSchmitz-TR-OAuth-2016.pdf

-- 
SEC - Institute of Information Security
University of Stuttgart
Phone +49 711 685 88468
Universitätsstraße 38 - 70569 Stuttgart - Room 2.434

---

From: John Bradley <[email-masked]>
Date: Fri, 18 May 2018 16:20:20 +0000

I am not against having "as" as REQUIRED.

While we are at it should we recommend that rfp be single use?

This draft hangs around as a ID and probably is not read by most people.

We may also want to mention it in security topics if we haven't.

I need to update this in the next couple of weeks to keep it from expiring anyway.

John B.

From: Daniel Fett
Sent: Friday, May 18, 3:46 PM
Subject: [OAUTH-WG] Reuse of "state" across different AS in draft-bradley-oauth-jwt-encoded-state-08
To: [email-masked]

Hi all,
Looking at draft-bradley-oauth-jwt-encoded-state-08, I see in Section 2 (https://tools.ietf.org/html/draft-bradley-oauth-jwt-encoded-state-08#section-2) that all contents of the JWT that would bind the specific state value to the AS are optional. This means that the following attack is possible (we described this attack in our OAuth paper [0], Section 5.1, albeit very briefly):
Precondition: A client that supports multiple AS (with one being an attacker-controlled AS, let's call it A-AS).
1. An honest user tries to authorize using A-AS, the attacker receives the value of "state" which is bound to the honest user's session with the client.
2. A-AS aborts the authorization (e.g., by redirecting the user back to the client's web site without any parameters).
3. The user starts a new authorization flow with an honest AS (H-AS).
Since the state JWT is not bound to the AS selected by the user, the state value generated in (1.) will likely be valid for the login flow started in (3.). The attacker therefore knows a valid state value and can mount a CSRF attack (which was supposed to be prevented by using state).
Solution: Always bind state to the chosen AS, i.e., make the "as" part of the JWT mandatory
-or- use some other mechanism on the client (if the client is stateful) to invalidate the old state value.

If we agree that this is problematic, I think this should be changed in draft-bradley-oauth-jwt-encoded-state and should at least briefly be discussed in the security topics document (I will be happy to write a section for the latter).
- Daniel
[0] https://sec.uni-stuttgart.de/_media/publications/FettKuestersSchmitz-TR-OAuth-2016.pdf
-- SEC - Institute of Information Security University of Stuttgart Phone +49 711 685 88468 Universitätsstraße 38 - 70569 Stuttgart - Room 2.434

---

From: Daniel Fett <[email-masked]>
Date: Sat, 19 May 2018 15:09:54 +0200

> While we are at it should we recommend that rfp be single use?  
If the state JWT is *not* signed and the client has no other means to
check the integrity of the JWT (e.g., by storing a copy in the browser's
session), rfp MUST be single use, where single use means that once a new
authorization request has been sent, all old "rfp" values (for that
browser session) become invalid.
(Rationale: The state reuse attack across AS would still be possible,
since an attacker could just replace the "as" value in an unsigned JWT.)

If the state JWT is signed and "as" is contained in the JWT, rfp does
not need to be single use in this sense.

To prevent defense-in-depth against the usage of leaked of state values,
in both cases, a state value that has once been accepted at the
redirection endpoint SHOULD be invalidated for future uses. In the the
case of an unsigned JWT, this means that a specific rfp value SHOULD
only be accepted once. If the JWT is signed, it would be sufficient to
memorize the hashes of used JWTs or their jti values, and decline JWTs
with the same hash/jti. (SHOULD since this is impossible for stateless
clients.)

(Overall, it seems like not signing state JWTs is not a good idea.)

> We may also want to mention it in security topics if we haven't. 
I will check the status there.
> I need to update this in the next couple of weeks to keep it from
I took a cursory look at the draft and I see two more issues:

(1)

> The "as" claim if present MUST correspond to the URI endpoint
registered as the "redirect_uri" for that AS.

This wording is not sufficient:
- The AS must be the expected AS (if there are means to check that, for
example the browser session).
- The redirection URI on which the authorization response was actually
received matches the "as" value.

(2)

> The client parses it as a JWT.  It then verifies the signature of the
JWT (if signed)

The client should check the signature if it expects the JWT to be
signed, not only if the received JWT is signed. I may be nitpicking
here, but developers should not be lead to believe that "if
signed(received_jwt): check_sig(received_jwt)" would be a good
implementation choice.

- Daniel

---

From: John Bradley <[email-masked]>
Date: Sat, 19 May 2018 15:12:10 +0200

Thanks

On Sat, May 19, 2018, 3:09 PM Daniel Fett <[email-masked]>
wrote:

> authorization request has been sent, all old "rfp" values (for that browser
> session) become invalid.
> If the state JWT is signed and "as" is contained in the JWT, rfp does not
> need to be single use in this sense.
>
> in both cases, a state value that has once been accepted at the redirection
> endpoint SHOULD be invalidated for future uses. In the the case of an
> unsigned JWT, this means that a specific rfp value SHOULD only be accepted
> once. If the JWT is signed, it would be sufficient to memorize the hashes
> of used JWTs or their jti values, and decline JWTs with the same hash/jti.
> (SHOULD since this is impossible for stateless clients.)
>
> > The "as" claim if present MUST correspond to the URI endpoint registered
> as the "redirect_uri" for that AS.
>
> The client should check the signature if it expects the JWT to be signed,
> not only if the received JWT is signed. I may be nitpicking here, but
> developers should not be lead to believe that "if signed(received_jwt):
> check_sig(received_jwt)" would be a good implementation choice.
>
> - Daniel
>
> --