Oauthlib: Client_secret and code_verifier (PKCE) should be transmitted securely

Created on 19 Apr 2019  ·  19Comments  ·  Source: oauthlib/oauthlib

client_secret and code_verifier are accepted when sent as parameters in query string

Request.client_secret should be checked for presence in headers or body and Request.code_verifier just in body but not query string as it is sensitive data.
Addition checks might be done, such as request type is POST and data was sent using HTTPS.

When client_secret or code_verifier is sent in query string it should result in Bad Request, enforcing client to send data securely.

Bug Contributor Friendly OAuth2-Provider

Most helpful comment

Hi @polamayster, you're perfectly right.

I'm adding the sections of RFC specifying how it has to be implemented:

Section of OAuth2.0 RFC :

https://tools.ietf.org/html/rfc6749#section-2.3.1

2.3.1.  Client Password
   Clients in possession of a client password
   MAY use the HTTP Basic authentication scheme (..) 

   Alternatively, the authorization server
   MAY support including the client credentials in the request-body (..)

   The parameters can only be transmitted in the request-body and
   MUST NOT be included in the request URI.

Section of PKCE RFC:

https://tools.ietf.org/html/rfc7636#section-4.5

4.5.  Client Sends the Authorization Code and the Code Verifier to the
      Token Endpoint
    In addition to the parameters defined in the OAuth 2.0 Access
    Token Request (Section 4.1.3 of [RFC6749]), it sends the following parameter:

   code_verifier
      REQUIRED.  Code verifier

https://tools.ietf.org/html/rfc6749#section-4.1.3


4.1.3.  Access Token Request

   The client makes a request to the token endpoint by sending the
   following parameters (..) in the HTTP request entity-body:

So I have no doubt about the pertinence of the issue. Any PRs are welcome!

All 19 comments

Hi @polamayster, you're perfectly right.

I'm adding the sections of RFC specifying how it has to be implemented:

Section of OAuth2.0 RFC :

https://tools.ietf.org/html/rfc6749#section-2.3.1

2.3.1.  Client Password
   Clients in possession of a client password
   MAY use the HTTP Basic authentication scheme (..) 

   Alternatively, the authorization server
   MAY support including the client credentials in the request-body (..)

   The parameters can only be transmitted in the request-body and
   MUST NOT be included in the request URI.

Section of PKCE RFC:

https://tools.ietf.org/html/rfc7636#section-4.5

4.5.  Client Sends the Authorization Code and the Code Verifier to the
      Token Endpoint
    In addition to the parameters defined in the OAuth 2.0 Access
    Token Request (Section 4.1.3 of [RFC6749]), it sends the following parameter:

   code_verifier
      REQUIRED.  Code verifier

https://tools.ietf.org/html/rfc6749#section-4.1.3


4.1.3.  Access Token Request

   The client makes a request to the token endpoint by sending the
   following parameters (..) in the HTTP request entity-body:

So I have no doubt about the pertinence of the issue. Any PRs are welcome!

I'll am interested in taking this on. Will submit a pull request soon.

Should this behaviour be enforced only for requests that expect credentials or any request in general?

To my understanding oauthlib.common.Request class has a __getattr__ method and _params dictionary (updated from query string and body parameters) and any request that tries to access/get client_secret or code_verifier should lookup only in body and/or headers (perhaps having separate property for sensitive data lookup would make sense)

I see, I'll submit a pr for it today

On Sat, Apr 20, 2019, 12:38 PM Bohdan <[email protected] wrote:

To my understanding oauthlib.common.Request class has a __getattr__
method and _params dictionary (updated from query string and body
parameters) and any request that tries to access/get client_secret or
code_verifier should only lookup in body and/or headers (perhaps having
separate property for sensitive data lookup would make sense)


You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/oauthlib/oauthlib/issues/666#issuecomment-485157051,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABKEVQNFJUFGDB5X24JBOJDPRNWKBANCNFSM4HHD7NNQ
.

To my understanding oauthlib.common.Request class has a __getattr__ method and _params dictionary (updated from query string and body parameters) and any request that tries to access/get client_secret or code_verifier should lookup only in body and/or headers (perhaps having separate property for sensitive data lookup would make sense)

So the checks should happen at attribute access time instead of when url is set? If request attributes are set just once at initialization, we can perform the checks right then instead of at every attribute access. Let me know if you think we should still be performing the checks at each attribute access.

I realize this issue is larger and applies to the whole /token endpoint. This endpoint MUST NOT accept any parameters in the URL. That's MUST NOT be allowed.

I think the introspection endpoint qualifies as well. IIRC, according to
HTTP specifications, POST requests must always ignore query parameters. If
we enforce that, we automatically solve all problems without affecting
valid use cases. I'm not sure how other HTTP verbs are supposed to behave
but I'm pretty sure on POST one. Can anyone confirm if that's the right
direction to go towards?

>

Denying all query parameters for all POST is a nice shortcut which is attractive ! However I am worried about the oauthlib's ResourceEndpoint and the fact that some of the users can still add query arguments to the URL (despite it is not recommended, it's still possible technically).

Can you elaborate a bit more on your concerns with resource endpoint?

The way I see it, POST requests are made either via form submissions or api
calls (writing explicit code). I do not see why would someone add partial
data as query params and partial as post data. In fact, it is easier to
commit completely to either.
Actually if you are using a library to make requests, it is far easier to
add everything as post body instead of splitting things up.

Users adding query params to POST if presented with an explanatory error,
should be able to self correct quickly.

Or maybe we can create a mixin or decorator that when applied, would raise
errors for POST requests with query params.

Here's another possible solution for it. For the endpoints (AuthorizationEndpoint, IntrospectEndpoint, RevocationEndpoint); If the request method is POST we can add the check for query params in their respective request validation methods . For (TokenEndpoint, MetadataEndpoint) we can add the check in response generation methods.
OR we can add the checking mechamism inside response generation method of all of them, for the sake of consistency. Does that sound like a good idea?

I think it's preferable to use the _request_ validation methods.
Knowing that POST is only for TokenEndpoint, IntrospectEndpoint and RevocationEndpoint. Others are GET: AuthorizationEndpoint, MetadataEndpoint.

A general comment though: should we focus on sensitive fields (e.g. maintaining a blacklist) or should we deny all OAuth2 parameters (we want NONE to be in the Query URI, right ?)

Ideally I'd like NONE in Query URI. I went for a blacklist because I wasn't
sure if I might be breaking some existing use cases. Not only are no OAuth2
parameters allowed, but no query parameters are allowed at all.

>

If you move the checks from Request to the Endpoints, I don't see any issues in disabling all query parameters for those Endpoints!

Alright then, I'll disable all query params on those exact endpoints. And
we are also restricting the http method to POST, correct?

While I understand the reasoning for this change, this seems to be a breaking change for clients that send client_secret as a query parameter. I would have expected backwards compatibility or a major version bump. Is this behavior intended?

We're currently using django-oauth-toolkit and clients are sending username, password, client_secret, client_id and grant_type as query parameter. If they switch to sending everything as POST parameter, they get the error unsupported_grant_type.

The support of query for POST is more a side-effect than the desired behavior. I understand it break your client, so I'd suggest to pinpoint to <3.1
Also, please consider to upgrade asap since it involves security concerns (secret are shown in logs, in proxy, across multiple unintended locations).

Was this page helpful?
0 / 5 - 0 ratings

Related issues

JonathanHuot picture JonathanHuot  ·  10Comments

ryarnyah picture ryarnyah  ·  3Comments

potiuk picture potiuk  ·  14Comments

ViktorHaag picture ViktorHaag  ·  11Comments

jcampbell05 picture jcampbell05  ·  14Comments