camaraproject / IdentityAndConsentManagement

Repository to describe, develop, document and test the Identity And Consent Management for CAMARA APIs
Apache License 2.0
18 stars 30 forks source link

Proposal to define a strict value for aud claim in the private_key_jwt #127

Closed mhfoo closed 1 month ago

mhfoo commented 4 months ago

Problem description Referring to the CIBA specs, the aud claim of the private_key_jwt can be any of the 3 options mentioned below. The proposal is to prevent reuse of the private_key_jwt at other endpoints.

The Client MUST authenticate to the Backchannel Authentication Endpoint using the authentication method registered for its client_id, such as the authentication methods from Section 9 of [OpenID.Core] or authentication methods defined by extension in other specifications. Note that there's some potential ambiguity around the appropriate audience value to use when JWT client assertion based authentication is employed. To address that ambiguity the Issuer Identifier of the OP SHOULD be used as the value of the audience. To facilitate interoperability, the OP MUST accept its Issuer Identifier, Token Endpoint URL, or Backchannel Authentication Endpoint URL as values that identify it as an intended audience.

Possible evolution Enforce the aud claim of the private_key_jwt to be the URL of the respective endpoint (as a string) at which the assertion is received, it must not be an item in an array. The URLs of the endpoint can be populated from the OpenID Provider's /.well-known/openid-configuration with the following metadata:

Example: If the endpoint at which the assertion is being received is the backchannel_authentication_endpoint, the Relying Party will populate the aud claim of the private_key_jwt to be the URL of the backchannel_authentication_endpoint as per the OpenID Provider's /.well-known/openid-configuration.

Alternative solution N.A

Additional context OIDC profile is being defined for CAMARA

garciasolero commented 4 months ago

From my point of view, it is not a good idea to limit the value of the aud claim to that of the endpoint to which the request is made. The CIBA specification is clear (Issuer Identifier, Token Endpoint URL, or Backchannel Authentication Endpoint), and forcing implementations to deviate from the standard by rejecting requests they should accept could jeopardize existing integrations.

If replay attacks are to be avoided, there are better mechanisms, such as the jti claim to prevent reuse of the token.

mhfoo commented 4 months ago

@garciasolero The concern is that the same signed JWT can be interchangeably use between the three endpoints:

The request object parameter (signed JWT) for /authorize can be used in /token? Is the above the correct understanding?

/token endpoint should be more secured as it allows client_credentials with scope. The aud claim can be seem similarly as a scope for access.

Understand we are defining a new profile for CAMARA OIDC and we should make things right.

mhfoo commented 4 months ago

@garciasolero

Proposed CAMARA OIDC - client-authentication profile states

The Audience MUST be the URL of the Authorization Server's Token Endpoint.

Please clarify on this point.

image

garciasolero commented 4 months ago

@mhfoo,

As I understand it, for JWT client assertion, it can only be interchangeable if you use the Authorization Server's Issuer or Token Endpoint as the audience. In the case of sending the requested endpoint as the audience (e.g. bc-authorize), other than Token Endpoint, it can only be used for that specific endpoint and not for any other.

If we agree on this, I can change the text in the self-contained profile to the following:

REQUIRED. Audience. The aud (audience) Claim. Value that identifies the Authorization Server as an intended audience. The Authorization Server MUST verify that it is an intended audience for the token. The Audience MUST be the URL of the Authorization Server's Issuer Identifier, Token Endpoint URL or Requested Endpoint URL.

AxelNennker commented 4 months ago

@mhfoo How about the text in this commit?

https://github.com/camaraproject/IdentityAndConsentManagement/blob/3353d10d371dd95bce6f8f9bc1dc9f3ae60d24a9/documentation/CAMARA-Security-Interoperability.md#client-authentication

Client Authentication

This CAMARA document allows one client authentication method, private_key_jwt, as defined in OIDC OIDC Client Authentication

OIDC Client Authentication defined the following about the audfield:

REQUIRED. Audience. The aud (audience) Claim. Value that identifies the Authorization Server as an intended audience. The Authorization Server MUST verify that it is an intended audience for the token. The Audience SHOULD be the URL of the Authorization Server's Token Endpoint.

This document defines that for OIDC Authorization Code Flow and OAuth2 Client Credentials Grant the audience MUST be the URL of the Authorization Server's Token Endpoint. This document defines that for OIDC CIBA the audience MUST be the Backchannel Authentication Endpoint.

mhfoo commented 4 months ago

@AxelNennker and @garciasolero

To be explicit, please refer to the table below:

S/N Invoking Endpoint aud = issuer metadata aud = token_endpoint metadata aud = backchannel_authentication_endpoint metadata aud = authorization_endpoint metadata other values
1 /token Reject Accept Reject Reject Reject
2 /bc-authorize Reject Reject Accept Reject Reject
3 /authorize Reject Reject Reject Accept Reject

Note: metadata is from the /.well-known/openid-configuration of the OpenID Provider

Context:

A) Both /token and /bc-authorize are using private_key_jwt. B) /authorize is using the request object parameter by value in the authorize request of the authorization code flow, as described in RFC9101. (Thanks to @garciasolero for this proposal to secure /authorize endpoint)

The signed JWT which is the request object parameter in B) is similar to A)’s signed JWT. In order to differentiate the use of the JWT, the aud should be the respective requested endpoint URL. It must not be the issuer.

The aud value must not be an array.

garciasolero commented 4 months ago

@AxelNennker and @mhfoo,

I think it is better to separate the use cases because some involve different types of JSON Web Tokens (JWTs). On one hand, we have the request object parameter, and on the other hand, we have the client assertion for authentication.

The value of aud should be the value of the authorization server (AS) issuer

Invoking Endpoint \ Audience issuer token endpoint bc-authorize endpoint any other endpoint
/token Accept Accept Reject Reject
/bc-authorize Accept Accept Accept Reject
AxelNennker commented 3 months ago

What does FAPI 2.0 say about private_key_jwt and aud? https://openid.bitbucket.io/fapi/fapi-2_0-security-profile.html.

5.3.2. Requirements for authorization servers

shall accept its issuer identifier value (as defined in [RFC8414]) either as the aud claim (when a string) or as a member of the aud claim (when an array) received in client authentication assertions

should accept its token endpoint url or the url of the endpoint at which the assertion was received, either as the aud claim (when a string) or as a member of the aud claim (when an array) received in client authentication assertions

5.3.3. Requirements for clients

if using private_key_jwt, shall use the authorization server's issuer identifier value (as defined in [RFC8414]) in the aud claim in client authentication assertions, and should send the issuer identifier value as a string, not as an item in an array;

@mhfoo @garciasolero is that a way forward for ICM?

garciasolero commented 3 months ago

The aud claim requirements for authorization servers in the FAPI profile are: issuer, token endpoint, or the endpoint URL (as I suggested above). I'm not sure why the client requirements section restricts sending only the issuer (and as a string, not in an array) when authorization servers are expected to accept the other values. In my opinion, I would include the requirements for the authorization server.

AxelNennker commented 3 months ago

The aud claim requirements for authorization servers in the FAPI profile are: issuer, token endpoint, or the endpoint URL (as I suggested above). I'm not sure why the client requirements section restricts sending only the issuer (and as a string, not in an array) when authorization servers are expected to accept the other values. In my opinion, I would include the requirements for the authorization server.

The section on clients is a "should", so clients can still use the other allowed values in FAPI 2.0 security.

The current text in #121 is this:

This document defines that for OIDC Authorization Code Flow and OAuth2 Client Credentials Grant the audience MUST be the URL of the Authorization Server's Token Endpoint. This document defines that for OIDC CIBA the audience MUST be the Backchannel Authentication Endpoint.

In other words, the aud is always the url the request is sent too. Which seems to be a simple rule, which is somewhat more secure than additionally allowing the "issuer" as well.

Invoking Endpoint \ Audience issuer token endpoint bc-authorize endpoint any other endpoint
/token Reject Accept Reject Reject
/bc-authorize Reject Reject Accept Reject

Please suggest "better" text if you think that is needed.

garciasolero commented 3 months ago

Suggested text aligned with what the CIBA standard and the FAPI profile state:

To facilitate interoperability, the Authorization Server MUST accept its issuer identifier, the token endpoint URL, or the URL of the endpoint at which the assertion was received as values of the audience claim.

mhfoo commented 2 months ago

@AxelNennker @garciasolero

The current text in https://github.com/camaraproject/IdentityAndConsentManagement/pull/121 is this:

This document defines that for OIDC Authorization Code Flow and OAuth2 Client Credentials Grant the audience MUST be the URL of the Authorization Server's Token Endpoint. This document defines that for OIDC CIBA the audience MUST be the Backchannel Authentication Endpoint.

In other words, the aud is always the url the request is sent too. Which seems to be a simple rule, which is somewhat more secure than additionally allowing the "issuer" as well.

Invoking Endpoint \ Audience issuer token endpoint bc-authorize endpoint authorize endpoint any other endpoint mulitple endpoints
/token Reject Accept Reject Reject Reject Reject
/bc-authorize Reject Reject Accept Reject Reject Reject
/authorize Reject Reject Reject Accept Reject Reject

Agree with Axel's statement above. The aud claim should not be an array.

The signed Object Request RFC 9101 is a signed JWT and it is communicated in the public domain over the Internet when performing the Auth Code flow.

The signed JWT maybe used to invoke the /token /bc-authorized endpoints by a bad actor, as mentioned in Cross-JWT Confusion.

As described in Section 2.8 of [RFC8725], attackers may attempt to use a JWT issued for one purpose in a context that it was not intended for. The mitigations described for these attacks can be applied to Request Objects.

One way that an attacker might attempt to repurpose a Request Object is to try to use it as a client authentication JWT, as described in Section 2.2 of [RFC7523]. A simple way to prevent this is to never use the client ID as the sub value in a Request Object.

The above proposal is leaving the API Consumer to secure the Operator's endpoint. As an Operator, we should secure our endpoints with restrictions.

Another way to prevent cross-JWT confusion is to use explicit typing, as described in Section 3.11 of [RFC8725]. One would explicitly type a Request Object by including a typ Header Parameter with the value oauth-authz-req+jwt (which is registered in Section 9.4.1). Note, however, that requiring explicitly typed Request Objects at existing authorization servers will break most existing deployments, as existing clients are already commonly using untyped Request Objects, especially with OpenID Connect [OpenID.Core]. However, requiring explicit typing would be a good idea for new OAuth deployment profiles where compatibility with existing deployments is not a consideration.

Finally, yet another way to prevent cross-JWT confusion is to use a key management regime in which keys used to sign Request Objects are identifiably distinct from those used for other purposes. Then, if an adversary attempts to repurpose the Request Object in another context, a key mismatch will occur, thwarting the attack.

Another approach is to issue different client_id for the same backend application when performing auth code flow and CIBA flow.

Bank XYZ will use client_id A for auth code flow, client_id B for CIBA flow. client_id A will not be accepted for CIBA flow, client_Id B will not be accepted for auth code flow.

AxelNennker commented 2 months ago

1) Regarding the following two sections:

Another approach is to issue different client_id for the same backend application when performing auth code flow and CIBA flow.

Bank XYZ will use client_id A for auth code flow, client_id B for CIBA flow. client_id A will not be accepted for CIBA flow, client_Id B will not be accepted for auth code flow.

@mhfoo Please do not add even more options to the mix.

2) Not sure why write about 9101. Yes, that is also a signed JWT, and there also can be a mixup-attack. Does your text after "The signed Object Request..." help with the solution if you assume that we assume that more concrete aud values are more secure?

To summarize this issue's discussion:

Specifying single, specific values for aud depending on the use-case and end point improves security. Doing that might possibly be bad for interoperability. Even though I am one of the editors of the CIBA standard I do not agree with the interoperability argument. Specifying the correct aud string is simple and I believe that clients are not overburdened with this task. While at the same time, we prevent mixup attacks.

You might challenge the "simple"-argument, but I think the other proposals (key management) are too complicated and implementing the aud-requirement discussed here is indeed simple.

Some examples for aud value in various implementations:

The example seem to use the endpoint url which agrees existing text.

AxelNennker commented 2 months ago
Invoking Endpoint \ Audience issuer token endpoint bc-authorize endpoint authorize endpoint any other endpoint mulitple endpoints
/token Reject Accept Reject Reject Reject Reject
/bc-authorize Reject Reject Accept Reject Reject Reject
/authorize Reject Reject Reject Accept Reject Reject

@garciasolero can TEF live with this table? As I said, I think that using the endpoint url is not obstacle for client developers and provides better security.

garciasolero commented 2 months ago

@AxelNennker

As I mentioned earlier, we should clearly separate the audiences for these endpoints because they have their own requirements: token and bc-authorize endpoints deals with client assertions for authentication, while the authorize endpoint deals with request objects.

In relation to the audience claim within a client assertion, we suggest what CIBA standard or FAPI profile define: to accept the issuer, the token endpoint and the endpoint URL as valid audiences to ensure interoperability.

CIBA:

Note that there's some potential ambiguity around the appropriate audience value to use when JWT client assertion based authentication is employed. To address that ambiguity the Issuer Identifier of the OP SHOULD be used as the value of the audience. To facilitate interoperability, the OP MUST accept its Issuer Identifier, Token Endpoint URL, or Backchannel Authentication Endpoint URL as values that identify it as an intended audience.

FAPI:

5.3.2. Requirements for authorization servers 5.3.2.1. General requirements [...]

  1. shall accept its issuer identifier value (as defined in [RFC8414]) either as the aud claim (when a string) or as a member of the aud claim (when an array) received in client authentication assertions;
  2. should accept its token endpoint url or the url of the endpoint at which the assertion was received, either as the aud claim (when a string) or as a member of the aud claim (when an array) received in client authentication assertions;

Of course, you can simplify the possible values and, for example, accept only the endpoint URL. However, this would affect already deployed solutions that follow the CIBA standard. Therefore, we suggest that the profile should allow authorization servers to accept these three values and recommend using the endpoint URL to clients.


Regarding what would happen if we support RFC9101 passing request objects by value, the standard already specifies what the value of the audience should be:

The value of aud should be the value of the authorization server (AS) issuer, as defined in [RFC8414].

In relation to your concern about the use of a JWT issued for a request object in authorize flow as a client authentication JWT, the RFC9101 already suggests a way to mitigate this by avoiding the client_id as sub value within the request object:

One way that an attacker might attempt to repurpose a Request Object is to try to use it as a client authentication JWT, as described in Section 2.2 of [RFC7523]. A simple way to prevent this is to never use the client ID as the sub value in a Request Object.

mhfoo commented 2 months ago

@AxelNennker

Not sure why write about 9101. Yes, that is also a signed JWT, and there also can be a mixup-attack. Does your text after "The signed Object Request..." help with the solution if you assume that we assume that more concrete aud values are more secure?

Regarding the above question, it is related to #128.

The following is the proposed text for PR #121 under OIDC Authorization Code Flow section.


The OIDC Authorization Code Flow is defined in OpenID Connect and incorporates RFC 9101, Passing a Request Object by Value.

AxelNennker commented 2 months ago

We are not making progress with this issue.

I changed the text regarding client authentication and the aud values to RECOMMEND instead of MUST.

The AZ still MUST ensure that it is the intended audience. The document, as always following the principle of not repeating the obvious, does not mention that fact because the standards are clear and no clarification is needed.

Client Authentication

This CAMARA document allows one client authentication method, private_key_jwt, as defined in OIDC OIDC Client Authentication

This document RECOMMENDS that for OIDC Authorization Code Flow and OAuth2 Client Credentials Grant the audience SHOULD be the URL of the Authorization Server's Token Endpoint. This document RECOMMENDS that for OIDC CIBA the audience SHOULD be the Backchannel Authentication Endpoint.