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

Adds OpenPubkey Support to SSH3 #146

Open
wants to merge 39 commits into
base: main
Choose a base branch
from

Conversation

EthanHeilman
Copy link
Contributor

@EthanHeilman EthanHeilman commented Jul 14, 2024

This PR adds an OpenPubkey authentication plugin to SSH3. OpenPubkey is a protocol that adds user held public keys to OpenID Connect (OIDC) while maintaining compatibility with existing OpenID Providers.

This provides a number of improvements to SSH3 over the current OIDC authentication in SSH3:

  1. Prevents replay attacks by signing TLS export materials: SSH3 OIDC authentication ID Tokens sent to one SSH3 server can be replayed by that server to authenticate that as that user to SSH3 servers. With OpenPubkey the ID Token (PK Token) is simply used as certificate, the SSH3 client can sign the export materials of the TLS session under the user's public key as specified in the PK Token removing the risk of replay attacks during OIDC authentication.

  2. Prevents MITM attacks via signature based authentication of the user: In SSH3 OIDC authentication, if the SSH3 server uses a self-signed certificate, an attacker can MITM the TLS session pretending to be the server, learn the user's ID Token and then send that to the real SSH3 server. This not only allows such an attacker to read and tamper with the communications between the SSH3 client and the SSH3 server, but also by learning the ID Token, the attacker can perform the replay attack above. With OpenPubkey this is not possible, but the MITM attacker can be impersonate the user to the SSH3 server.

One way to think about the security offered by the SS3 OpenPubkey auth plugin is that it gets to the security offered by the SSH3 public key plugin, but with OpenID Connect.

Support for this OpenPubkey in SSH3 would allow SSH3 to leverage OpenPubkey's MFA (webauthn) feature in SSH3. This feature is out of scope for this PR.

This feature in OpenPubkey is being tracked as: openpubkey/openpubkey#202

Demo

Short video of using OpenPubkey as an authentication plugin for SSH3

Screen.Recording.2024-08-20.at.8.43.22.PM.mov

~/.ssh3/authorized_identities on the remote server

openpubkey 992028499768-ce9juclb3vvckh23r83fjkmvf1lvjq18.apps.googleusercontent.com https://accounts.google.com [email protected]

The local command to run openpubkey auth on SSH3

./ssh3 -openpubkey https://accounts.google.com [email protected]:443/ssh3-term

How it works

Our design for the OpenPubkey auth plugin uses the pattern established by SSH3's public key auth. The SSH3 client generates a JWT which attests to the TLS export materials of the current TLS session, it signs this JWT under the user's public key and then puts the JWT in the HTTP Authorization header. In OpenPubkey the public key is the public key in the PK Token (ID Token with additional metadata).

SSH3 OpenPubkey plugin diagram

More specifically:

  1. SSH3 client: Creates and calls the OpenPubkey client plugin
    1. OpenPubkey client: generates an ephemeral key pair (upk, usk) for the user.
    2. OpenPubkey client: triggers the auth OIDC flow to the OP (OpenID Provider), typically this involves opening the user's browser to the OP and authenticating.
    3. OpenPubkey client: Receives ID Token for the user from the OP. This ID Token commits to user's public key, upk.
    4. OpenPubkey client: Packages this ID Token with other public key metadata to generate a PK Token for the user.
  2. SSH3 client:
    1. Opens TLS session to SSH3 server,
    2. Extracts conversation ID, a.k.a. TLS export materials, from TLS session,
    3. Creates a JWT, SSH3-JWT (shown below), which includes the conversation ID under the key jti.
    4. The SSH3 client then signs the SSH3-JWT as an OSM (OpenPubkey Signed Message) using the user's signing key, usk. OSMs are a type of PK Token aware JWT/JWS which includes additional metadata in the protected header of the signed object. For instance OSMs always includes the hash of the PK Token under which they should be verified so that they are cryptographically bound to a specific to PK Token.
    5. Appends the SSH3-JWT and PK Token together and sets it as HTTP Authorization header authorization=Compact(SSH3-JWT)#Compact(PK Token).
    6. Sends the HTTP request to the SSH3 server.
  3. SSH3 server:
    1. Inside the TLS session the SSH3 server receives the HTTP request from the SSH3 client and extracts the authorization header and sends it to the SSH3 OpenPubkey server plugin.
    2. The SSH3 OpenPubkey plugin checks that:
      1. The TLS session between the SSH3 server and the SSH3 client matches the conversation ID specified in the SSH3-JWT sent in the authorization header.
      2. The SSH3-JWT is well formed and unexpired.
      3. That the signature on the SSH3-JWT verifies using the user's public key, upk, in the PK Token. This verification uses the OSM (OpenPubkey Signed Message) verification procedure, which ensures that not only does the SSH3-JWT verify under the public key in the PK Token, but also that the SSH3-JWT contains the hash of the PK Token that signed it.
      4. Next the OpenPubkey server plugin finds any OpenPubkey identities in the authorized identities file and checks if any of these match the OIDC identity in the PK Token provided in the authorization header. If a match is found, the server verifies the PK Token with the matching OpenID Provider.
  4. SSH3 server: If all of the above checks pass the server has successfully authenticated the user.

JWT format used by OpenPubkey auth plugin

This is the same format as used by SSH3 pubkey auth. For the username Alice the JWT format is:

{
  "iss":       "Alice",
  "iat":       "time.Now()",
  "exp":      "time.Now().Add(10 * time.Second)",
  "sub":       "ssh3",
  "aud":       "unused",
  "client_id": "ssh3-Alice",
  "jti":       "base64ConversationId aka TLS export material",
}

Rationale for distinguishing between OpenPubkey and OIDC in Authorized Identities

SSH3 uses an authorized_identities file to configure policy for which identities defined in OpenID Connect are allowed to connect to the remote server.

To distinguish OpenPubkey identities from OpenIDConnect identities we set openpubkey as the first part of an authorized identity rather than oidc. This distinction is important to security as OpenPubkey PK Tokens contain OpenID Connect ID Tokens. Thus, if a SSH3 server was configured to accept either OpenPubkey PK Tokens or OpenID Connect ID Tokens, we would lose the security advantages offered by OpenPubkey as the ID Token in the PK Token could simply be replayed as standard OpenID Connect ID Token.

Consider an attacker Eve who learned the PK Token of anon.author.aardvark

This authorized_identities would not protect against replay attacks because Eve could take the ID Token contained in anon.author.aardvark PK Token as an OpenID Connect authentication value.

oidc audienceA https://accounts.google.com [email protected]
openpubkey audienceA https://accounts.google.com [email protected]

This authorized_identities protects against replay attacks because Eve couldn't use the ID Token contained in anon.author.aardvark PK Token as an OpenID Connect authentication value since the audiences don't match: audienceA !=audienceB. The same would be true if the issuers didn't match or the email addresses didn't match.

oidc audienceA https://accounts.google.com [email protected]
openpubkey audienceB https://accounts.google.com [email protected]

This authorized_identities prevents the attacker because it will reject OpenID Connect authentication. Only using This is the most secure configuration.

openpubkey audienceA https://accounts.google.com [email protected]

If for compatibility purposes OpenPubkey and OpenID Connect are used for the same audience, issuer and user, the downside is simply that OpenPubkey's security degrades to security of OpenID Connect.

GQ/ZK Signatures

If OpenPubkey GQ or ZK Signatures are used then the above replay attacks are completely eliminated. In this case there would not be a benefit to distinguishing between OpenID Connect and OpenPubkey in authorized identities. I am avoiding using GQ or ZK signatures in SSH3 because I want to keep the implementation simple. It may be worth rethinking that decision when deciding on the final format of authorized_identities.

francoismichel and others added 27 commits March 25, 2024 13:41
@EthanHeilman EthanHeilman marked this pull request as draft July 14, 2024 19:42
@EthanHeilman EthanHeilman changed the title Adds OpenPubkey Support to SSH3 [WIP] Adds OpenPubkey Support to SSH3 Aug 21, 2024
@EthanHeilman EthanHeilman marked this pull request as ready for review September 10, 2024 21:31
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 this pull request may close these issues.

2 participants