Open almereyda opened 6 months ago
I was going to open a ticket but I see there is already an existing ticket.
The Google OP requires a static redirectURI and does not allow wildcards. Google does let you wildcard localhost ports. The redirect URI http://localhost/
will work for all ports.
This is the error I get when I use SSH3 oidc with google:
Is the security goal of adding a random fragment to securely link the request to the OP with the response form the OP? If so OIDC already provides a more secure way of doing this using PKCE (RFC 7636: Proof Key for Code Exchange). The way it works is client creates:
$$CV \gets \{0,1\}^{256}$$
$$VC \gets \mbox{SHA256(CV)}$$
The client then adds VC to the auth request URI. Then to claim the ID Token associated with this request, the client must reveal VC to server. This means that even if an attacker intercepts the original request, the attacker only learns VC not CV. Additionally if the attacker triggers a redirectURI response which is not intended response from the client, when the client-supplies VC, it will break.
It looks like SSH3 supports PKCE, but the default in the SSH3 config is to for PKCE to be false.
doPKCE := flag.Bool("do-pkce", false, "if set perform PKCE challenge-response with oidc")
I don't see a reason why you would ever want PKCE to be false. Is there an OP that doesn't support PKCE?
In OpenPubkey we support choosing a port from a list of ports. This allows parties to add that list of ports to their redirectURI list if needed and also allows us to fail over if another application is already bound to that port. It might be worth copying this pattern to support OPs that don't wildcard localhost ports.
The random port is also problematic for firewalls
@almereyda If you want to play around SSH OIDC with a fix redirectURI, I created a PR #144 You can just pull from my branch and use it.
One reason for using that randomly-generated path was to avoid other programs to hijack the token by sending a fake request to the ssh3 waiting on localhost, causing it to close the socket, and then the attacker could listen on the same port and retrieve the token like that. It seems doable on pretty much any OS. But maybe am I missing something making such an attack undoable ?
@francoismichel If I understand your attack correctly, this was the attack that PKCE was introduced to defeat:
An attacker who intercepts the auth code by listening on the localhost socket can not redeem that auth code for an ID Token because the attacker does not know a value CV such that VC=Hash(CV).
This is the value of enabling PKCE.
There is another known attack here which neither PKCE nor a randomized redirect URI does not fix.
The user looking at a browser window that popped up has no easy way to determine that this was opened by the legitimate service running on localhost. There two mitigations:
Mobile device flows don't have this problem because app identity is known to the OS and so the iOS can enforce a map of redirect URIs to specific cryptographic identities associated with the candy crush app. There are some other solutions in this space, but using I favor the OpenPubkey cosigner approach.
In general it is very hard to protect secrets like this if an attacker has a programming process on your endpoint that can open ports. That can do so much bad stuff, that provided security against such a threat is diminishing returns.
Why release the port at all? To be the same redirect URL and work best with reverse proxies and firewalls the port should be static anyhow. So I see no reason why the port should be release after using it?
@septatrix Releasing the port makes sense if you are using this as a cli where the cli isn't always running. The alternative is a daemon which starts at OS bootup and always holds that port or a set of ports, but that is a more complex client to build.
@septatrix Releasing the port makes sense if you are using this as a cli where the cli isn't always running. The alternative is a daemon which starts at OS bootup and always holds that port or a set of ports, but that is a more complex client to build.
Ah I was under the assumption that the SSH3 server would be the one receiving the token. That seems like the intuitive solution
Thanks for the discussion. The attack with an attacker opening a browser window concurrently to SSH3 may not be easy to perform but it makes sense. I wish there was a generic way in OIDC to display some text to the user in the browser window so that it can check the browser window is indeed the right one. The difference in this scenario however, is that the attacker must either be root or be running with the same rights as the current user in order to be able to show a browser window in its desktop session, while the previous scenario (the one avoidable with PKCE), the attacker just needs to be able to listen on a port.
The reason why PKCE is disabled by default is simply because I couldn't make it to work by the time with that specific version of the oidc module and the google OP, but I may give it another try now. We should probably list the common OPs that support PKCE well and enable it by default.
Concerning the random URL, I am not strongly against removing it, but we should probably ensure that PKCE is enabled then, and use the random URL if not.
I would also like to note that the RFC says to construct these loopback URLs using the numeric literals (127.0.0.1 and [::1] respectively) instead of "localhost".
Another option (which would also be necessary when one wants to implement a mobile client for SSH3) is to use private-use URI schemes like be.francoismichel.ssh3:/oauth2redirect/
. This is also possible on desktop OS though it requires a more traditional installation. E.g. on linux a ssh3.desktop
file would be required
@septatrix My two cents:
@septatrix My two cents:
- Using numeric loopbacks seems reasonable. The only reason I like localhost is that is it generic to ipv4 and ipv6. It seems like it wouldn't be hard to just specify two redirect URIs one being the loopback for IPv4 and one being the loopback for IPv6.
In Linux binding to ::1 defaults to dual stack mode, i.e. it also received IPv4 packets
- How well supported is native-app OAuth in OIDC on the desktop? I haven't build any systems using it and I don't know if there are any gotchas. I agree on mobile devices private-use schemes is both well supported and a security requirement. Are protocol handlers still used for this?
Not really sure about this TBH
The current implementation of the OIDC client in the
ssh3
client binary spawns a local HTTP webserver at a random port with a random URL path fragment. This contradicts the OIDC specification, which expects a persistent and static (list of) redirect URI(s), even when the application is set to confidential.https://github.com/francoismichel/ssh3/blob/20f2894426742ee2fe92e33fa3d2521be7677b20/auth/openid_connect.go#L46-L56
This way it is not possible to set up an OIDC client application with an ID and secret pair that works, e.g. with GitLab.
OIDC Provider:
$(openssl rand -hex 32)
, is generated by the identity provider, e.g.6f1379417c8a5cae738bc030e5ddc59ec352af02704351e182727fb0136fe1fe
gloas-$(openssl rand -hex 32)
, is generated in the identity provider, here GitLab, e.g.gloas-48ac71b03fcf805a127ba614c88646956751e154c76b77991f909a918c21b86e
http://localhost/
openid
,email
Notes:
localhost
. Then PKCE authentication is required.Server configuration:
Client configuration:
The SSH3 server has been started first with
to generate the key pair.
Subsequent invocations fail, when the
-generate-selfsigned-cert
flag is passed, why the final command line reads:When spawning an associated SSH3 client with
the OIDC provider, here GitLab, responds with:
This also happens when using the PKCE authentication scheme:
Primary ways of mitigating this could be:
Additionally there may be a way to get rid of the
client_secret
on the client in case of only supporting PKCE authentication for non-confidential OIDC clients, e.g. by introducing a flat-require-pkce
on the server.References:
99