Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pkce does not seem to work #1512

Closed
leo-baltus opened this issue Aug 5, 2019 · 20 comments · Fixed by #1517 or #1567
Closed

pkce does not seem to work #1512

leo-baltus opened this issue Aug 5, 2019 · 20 comments · Fixed by #1517 or #1567

Comments

@leo-baltus
Copy link

Describe the bug

Running hydra v1.0.0 with a postgresql backend, its seems that code_challenge_method, code_challenge and code_verifier are ignored, as it does not make any difference how right or wrong it is used.

Reproducing the bug

curl 'https://auth.hydra.xxx.nl/oauth2/auth?access_type=offline&client_id=radio1-app&code_challenge=ahsbr8liNiieY9JKiSoX0EEVK52xrlGCFvM-zG-Tuhc&code_challenge_method=S256&redirect_uri=http%3A%2F%2F127.0.0.1%3A5555%2Fcallback&response_type=code&scope=openid+profile+email&state=I+wish+to+wash+my+irish+wristwatch'

This should be a valid challenge, the resulting code-to-access-token exchange is always sucessfull no matter how wrong my code_verifier is.

Also setting e.g. code_challenge_method=S257 does not result in some form of error.

Server logs

logs showing how S257 is allowed:

{"level":"info","method":"GET","msg":"started handling request","remote":"145.58.114.228","request":"/oauth2/auth?access_type=offline\u0026client_id=radio1-app\u0026code_challenge=_RpfHqw8pAZIomzVUE7sjRmHSM543WVdC4o-Kc4_3C0\u0026code_challenge_method=S257\u0026redirect_uri=http%3A%2F%2F127.0.0.1%3A5555%2Fcallback\u0026response_type=id_token\u0026scope=openid+profile+email\u0026state=I+wish+to+wash+my+irish+wristwatch","request_id":"244f22dfd4acb49e867323294257871d","time":"2019-08-05T16:04:49Z"}
{"level":"info","measure#hydra/public: https://auth.hydra.xxx.nl/.latency":10941200,"method":"GET","msg":"completed handling request","remote":"145.58.114.228","request":"/oauth2/auth?access_type=offline\u0026client_id=radio1-app\u0026code_challenge=_RpfHqw8pAZIomzVUE7sjRmHSM543WVdC4o-Kc4_3C0\u0026code_challenge_method=S257\u0026redirect_uri=http%3A%2F%2F127.0.0.1%3A5555%2Fcallback\u0026response_type=id_token\u0026scope=openid+profile+email\u0026state=I+wish+to+wash+my+irish+wristwatch","request_id":"244f22dfd4acb49e867323294257871d","status":302,"text_status":"Found","time":"2019-08-05T16:04:49Z","took":10941200}

Server configuration

log:
  level: info
  format: json
serve:
  public:
    port: 4444
    host:
    cors:
      enabled: false
    access_log:
      disable_for_health: false
  admin:
    port: 4445
    host:
    cors:
      enabled: false
    access_log:
      disable_for_health: false
  tls:
dsn: postgres://hydra:password@sqlproxy:5432/hydradb?sslmode=disable
webfinger:
  jwks:
    broadcast_keys:
      - hydra.openid.id-token # This key is always exposed by default
  oidc_discovery:
    client_registration_url: https://my-service.com/clients
    supported_claims:
      - email
      - username
    supported_scope:
      - email
      - whatever
      - read.photos
    userinfo_url: https://example.org/my-custom-userinfo-endpoint
oidc:
  subject_identifiers:
    enabled:
      - pairwise
      - public
    pairwise:
      salt: some-random-salt
  dynamic_client_registration:
    default_scope:
      - openid
      - offline
      - offline_access
urls:
  self:
    issuer: https://auth.hydra.xxx.nl
    public: https://auth.hydra.xxx.nl
  login: https://login.hydra.xxx.nl/login
  consent: https://login.hydra.xxx.nl/consent
  post_logout_redirect: https://usabilitygeek.com/ux-logout-lapse/
strategies:
  scope: DEPRECATED_HIERARCHICAL_SCOPE_STRATEGY
ttl:
  login_consent_request: 1h
  access_token: 1h
  refresh_token: 720h
  id_token: 1h
  auth_code: 10m
oauth2:
  expose_internal_errors: true
  hashers:
    bcrypt:
      cost: 10
secrets:
  system:
    - this-is-the-primary-secret
    - this-is-an-old-secret
    - this-is-another-old-secret
  cookie:
    - this-is-the-primary-secret
    - this-is-an-old-secret
    - this-is-another-old-secret
tracing:
  provider:

Expected behavior

I expected an error on getting the accesstoken using a false code_verifier
Also I expect errors when using unsupported code_challenge_method or too short code_challenge's

Environment

Docker/kubernetes

  • v1.0.0
@aeneasr
Copy link
Member

aeneasr commented Aug 5, 2019

Is token_endpoint_auth_method set to none for radio1-app?

@leo-baltus
Copy link
Author

No, it was left at the default client_secret_basic. After setting it to none:

failed to get token: oauth2: cannot fetch token: 401 Unauthorized
Response: {"error":"invalid_client","error_description":"Client authentication failed (e.g., unknown client, no client authentication included, or unsupported authentication method)","error_hint":"The OAuth 2.0 Client supports client authentication method \"none\", but method \"client_secret_post\" was requested. You must configure the OAuth 2.0 client's \"token_endpoint_auth_method\" value to accept \"client_secret_post\".","status_code":401}

I have tried the same setup with Okta and Cognito, they work like this out-of-the-box. I.e. code-grant and code_challenge.

@aeneasr
Copy link
Member

aeneasr commented Aug 6, 2019 via email

@leo-baltus
Copy link
Author

For a public client, using a client secret obviously is not a wise thing to do. Im my case I am exploring PKCE in a private client so no issues with that, it might even enhance security.

Maybe PKCE and public clients can be regarded as two separate things? That way all clients can benefit from PKCE.

@aeneasr
Copy link
Member

aeneasr commented Aug 6, 2019 via email

@leo-baltus
Copy link
Author

Consider the case of client behind a loadbalancer which does tls termination leaving the last part of the connection (LB to client) over plain http. I think a client could benefit from the extra measure. Hydra would also be on par with Okta, Cognito etc.

@aeneasr
Copy link
Member

aeneasr commented Aug 6, 2019 via email

@leo-baltus
Copy link
Author

Funny how https://tools.ietf.org/html/rfc7636 exactly specifies my example:

In this attack, the attacker intercepts the authorization code
returned from the authorization endpoint within a communication path
not protected by Transport Layer Security (TLS), such as inter-
application communication within the client's operating system.

The outgoing connection is protected by TLS, so no MITM there. The incoming connection however is vulnerable to MITM but an attacker does not have the verifier to get the access token.

@aeneasr
Copy link
Member

aeneasr commented Aug 6, 2019

No, the scenarios are completely separate. In the spec, the redirect url is not protected by TLS because it's using a custom redirect scheme (the only scenario where no TLS is allowed for Hydra). A malicious app can register on that scheme and receive the code BEFORE the legitimate app.

In your scenario, there is no TLS connection between the LB and the server side app. A MITM could therefor listen in on all the traffic, including the result of the code exchange.

We are going in circles and there is no productive outcome. I'll make a patch to address your initial issue (using PKCE should throw an error when a private client is being used) but that's as much time as I can dedicate to discuss this.

@leo-baltus
Copy link
Author

In your scenario, there is no TLS connection between the LB and the server side app. A MITM could therefor listen in on all the traffic, including the result of the code exchange.

We may be talking about different technologies but a LB is different from e.g. VPN, only incoming connections are proxied the client so outgoing calls are initiated over separate connections making their own tls handshake so code exchange is not going through the loadbalancers.

@aeneasr
Copy link
Member

aeneasr commented Aug 6, 2019

We may be talking about different technologies but a LB is different from e.g. VPN, only incoming connections are proxied the client so outgoing calls are initiated over separate connections making their own tls handshake so code exchange is not going through the loadbalancers.

In that case you still need the client secret (not known to the attacker) to exchange the code. Again, this attack only makes sense in the case where the client can not keep a secret confidential - mobile apps, desktop apps, and so on.

@leo-baltus
Copy link
Author

Again, these are separate concerns. A public client cannot keep a client secret because it is a public client. A private client can be vulnerable to code hijacking if its incoming requests is not protected by TLS. They both can benefit from PKCE. In fact I do not see why modern private clients would not use PKCE in all cases.

@aeneasr
Copy link
Member

aeneasr commented Aug 6, 2019

Private clients are not vulnerable to code hijacking because the hijacker does not have the client secret, which is required for the exchanging the code for the token. PKCE is a substitute for the client secret. Period.

aeneasr added a commit that referenced this issue Aug 6, 2019
Using PKCE with private clients now returns an error message.

Closes #1512
aeneasr added a commit that referenced this issue Aug 6, 2019
Using PKCE with private clients now returns an error message.

Closes #1512
@leo-baltus
Copy link
Author

https://tools.ietf.org/html/draft-ietf-oauth-security-topics-09#section-2.1.1

Note: although PKCE so far was recommended as a mechanism to protect
native apps, this advice applies to all kinds of OAuth clients,
including web applications.

@aeneasr
Copy link
Member

aeneasr commented Aug 19, 2019

Sorry, completely overlooked this :) Thank you for digging this up! Will change behavior accordingly!

@aeneasr aeneasr reopened this Aug 19, 2019
@ghost
Copy link

ghost commented Aug 19, 2019

Yes, even if it is recommended for maximum security to implement PKCE on all platforms. It requires currently some iframe hacks to manage correctly local redirect management within the same context between parent and child iframe (it is annoying for those who implemented correctly CSPv2, to reduce our CSP security (local references) just to implement PKCE in Web).

@thibaultponcelet
Copy link

Hi, In order to follow https://tools.ietf.org/html/draft-ietf-oauth-security-topics-09#section-2.1.1 recommendation we also want to enable PKCE on private clients.

Is there already a timeline on when is this going to be available in Hydra @aeneasr?
We could also help on this if it is not but we have nobody fluent in Go in our team so it could take some time...

Thanks a lot

@aeneasr
Copy link
Member

aeneasr commented Aug 26, 2019

No timeline at the moment as we have iceboxed new features on our end for the next two months. However, we'd love a PR - you can probably work along the lines of: ory/fosite#375

@thibaultponcelet
Copy link

Thank you for your quick answer. We will see if we can manage to do it ourselves or not

@thibaultponcelet
Copy link

We fixed it in fosite here: ory/fosite#382

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants