- Un jeton de sécurité signe à l’intérieur de l’appareil sans jamais exporter la clé privée, et exige une action physique de l’utilisateur, ce qui rend difficile pour un attaquant distant de produire des signatures arbitraires
- Il peut servir à l’authentification SSH, à l’U2F, à la connexion locale sans mot de passe, à
sudo et à la signature des commits git, et les dispositifs de sécurité intégrés des ordinateurs portables et smartphones récents peuvent remplacer une YubiKey
- Le fichier de « clé privée » créé avec
ssh-keygen -t ed25519-sk n’est pas la vraie clé privée, mais un handle qui pointe vers la clé stockée dans le jeton ; avec le même jeton, on peut générer le même fichier de clé SSH sur un autre ordinateur
- Sur MacBook, il a été possible de configurer le secure element comme clé SSH pour activer une connexion SSH via Touch ID, et pour la signature des commits git il a fallu utiliser
ssh-agent et une configuration user.signingKey au format key:: plutôt qu’un chemin de fichier
- Les jetons de sécurité ne permettent pas de récupérer la clé privée en cas de perte et posent un risque d’usage lié à l’habitude de toucher l’appareil à répétition ; sur les PC portables Windows, Windows Hello pouvait confirmer l’usage d’une clé SSH par reconnaissance faciale, empreinte ou PIN
Avantages et limites des jetons de sécurité
-
Une architecture clé pour bloquer les attaques à distance
- Un jeton de sécurité conserve la paire clé privée/clé publique dans l’appareil ; la clé publique s’exporte facilement, mais la clé privée ne sort jamais de l’appareil
- Quand on envoie à l’appareil le paquet de données à signer, la signature est produite à l’intérieur avec la clé privée, et cela demande en général une action physique de l’utilisateur, comme appuyer sur un bouton tactile clignotant
- Même si un attaquant distant prend le contrôle de l’ordinateur, le jeton de sécurité ne produira pas de signature arbitraire tant que l’utilisateur n’agit pas physiquement dans le monde réel ; cela paraît donc préférable à une approche où l’on laisse une paire complète de clés SSH privées/publiques sous forme de fichiers dans
~/.ssh
- Il existe aussi des options pour ceux qui veulent un firmware FOSS, comme SoloKeys et Nitrokeys
- Les jetons de sécurité plus haut de gamme ajoutent parfois des fonctions biométriques, comme un lecteur d’empreintes intégré, mais le point essentiel reste que la clé privée ne quitte jamais l’appareil
-
Les risques liés à l’usage
- Si l’utilisateur s’habitue à appuyer chaque fois que le jeton de sécurité clignote, il peut aussi répondre machinalement à une requête malveillante
- Lors de suites de signatures successives, quand il faut toucher le jeton de façon répétée, il peut devenir difficile de remarquer qu’une demande supplémentaire clignote
- Apple et Microsoft utilisent sur les applis d’authentification mobile un système où un code numérique aléatoire est affiché à chaque requête d’accès et doit être saisi par l’utilisateur, mais c’est plus contraignant et cela réduit l’avantage ergonomique des jetons de sécurité par rapport à des applis comme Authy ou Google Authenticator utilisant TOTP
-
Perte et sauvegarde
- Si l’on perd un jeton de sécurité, la clé privée correspondante disparaît définitivement et il n’existe aucun moyen de la sauvegarder
- Pour éviter de se retrouver bloqué sur plusieurs comptes, mieux vaut acheter au moins deux jetons de sécurité et les enregistrer sur les mêmes services
- Une alternative consiste à utiliser un système de sauvegarde et de restauration comme BIP 39, qui convertit la clé privée en une liste de mots lisibles par un humain
- Si la clé privée pouvait sortir de la Secure Enclave, cela ouvrirait aussi la voie à des attaques de phishing visant à pousser l’utilisateur à noter cette liste de mots au mauvais endroit
- Si la possibilité de perdre tous ses jetons de sécurité est une vraie inquiétude, la liste de mots BIP 39 peut devenir le dernier recours pour récupérer l’accès au système
Utiliser un jeton de sécurité avec SSH et git
-
Mettre la clé privée SSH dans un jeton de sécurité
- En temps normal, l’exécution de
ssh-keygen crée une paire de fichiers contenant la clé privée complète
- Pour placer la clé privée dans un jeton de sécurité, il faut installer libfido2 en suivant les instructions FIDO/U2F de Yubico, puis exécuter
ssh-keygen -t ed25519-sk avec le jeton branché
- Là aussi, une paire de fichiers est créée, mais le fichier de « clé privée » n’est pas la vraie clé privée : c’est un handle qui pointe vers la clé privée stockée dans le jeton
- Si l’on relance
ssh-keygen -t ed25519-sk avec le même jeton de sécurité, on peut régénérer la même paire clé privée/clé publique sur n’importe quel ordinateur ; l’accès SSH se déplace donc avec le jeton de sécurité plutôt qu’avec un fichier précis sur une machine donnée
-
Authentification git et signature des commits
- Environ 90 % des situations où il faut toucher le jeton de sécurité viennent de l’usage de git
- Les forges git implémentent l’authentification SSH pour les opérations push et pull, et il suffit d’y téléverser le fichier
id_ed25519_sk.pub généré plus haut pour autoriser cette paire de clés liée au jeton de sécurité
- git prend aussi en charge les clés SSH pour la signature des commits ; en suivant la documentation GitHub Configurer votre clé de signature avec une clé SSH, puis en exécutant
git config --global commit.gpgsign true, tous les commits sont signés automatiquement
- Pour qu’une forge git reconnaisse les commits comme étant signés par vous, il faut téléverser à nouveau la clé publique, généralement dans un champ distinct de celui utilisé pour l’authentification SSH
-
Les inconvénients de la signature des commits
- Lors d’un rebase d’une longue série de commits, il faut tous les signer à nouveau
- Sur une YubiKey équipée d’un lecteur d’empreintes, le taux d’échec de reconnaissance était trop élevé pour signer des dizaines de commits d’affilée, au point d’abandonner cet usage
- Le wrapper centré sur “rebase/amend” de git, jujutsu, propose une méthode pour signer les commits uniquement au moment du push
-
Connexion locale Linux et sudo
Utiliser le secure element du MacBook comme clé SSH
- Laisser un jeton de sécurité branché en permanence sur un port USB-C revient à laisser dépasser un petit levier qui peut endommager à la fois le port et le jeton en cas de chute ou de choc
- Sur un MacBook Air M1 de 2020, le secure element intégré a été configuré comme clé SSH en suivant les instructions d’Arian van Putten
sc_auth create-ctk-identity -l ssh -k p-256-ne -t bio
ssh-keygen -w /usr/lib/ssh-keychain.dylib -K -N ""
- Cette commande crée une paire de fichiers clé privée/clé publique
id_ecdsa_sk_rk, ensuite déplacée dans le répertoire ~/.ssh
- Là encore, le fichier de clé privée n’est pas la vraie clé privée mais un handle vers la clé stockée dans l’appareil, ce qui lui donne une forme que l’on peut coller publiquement
- Pour ajouter la clé publique comme authorized key sur un serveur de homelab, il faut exécuter :
ssh-copy-id -i ~/.ssh/id_ecdsa_sk_rk.pub <server nickname>
- Puis ajouter la configuration suivante dans
~/.ssh/config
Host *
IdentityFile ~/.ssh/id_ecdsa_sk_rk
SecurityKeyProvider=/usr/lib/ssh-keychain.dylib
- Quand on lance
ssh <server nickname>, macOS affiche automatiquement une demande d’empreinte avant la connexion, puis l’authentification SSH se déroule normalement
Signer des commits git avec le secure element du MacBook
- Même après avoir défini
git config --global user.signingKey /Users/ahelwer/.ssh/id_ecdsa_sk_rk et mis à jour le fichier .ssh/allowed_signers, la signature des commits git ne fonctionne pas immédiatement
- git échoue à signer les commits et affiche une erreur du type
device not found?
error: Signing file /var/folders/l5/5wqvq2l10p96wtdtfr6lvrvw0000gn/T//.git_signing_buffer_tmpc4uQgO
Confirm user presence for key ECDSA-SK SHA256:oQDA2SNYb2MoSQcxJVSmWyAeAWPqMp7rxliBRfi87as
Couldn't sign message: device not found?
Signing /var/folders/l5/5wqvq2l10p96wtdtfr6lvrvw0000gn/T//.git_signing_buffer_tmpc4uQgO failed: device not found?
fatal: failed to write commit object
- La solution consiste à utiliser ssh-agent au lieu de pointer directement vers les fichiers du répertoire
~/.ssh
- En suivant le tutoriel ci-dessus, on enregistre la paire de clés dans ssh-agent avec la commande suivante
ssh-add -K -S /usr/lib/ssh-keychain.dylib
- Ensuite, dans
user.signingKey, il faut mettre non pas un chemin de fichier mais la clé elle-même, précédée de key::, en reprenant le contenu de ~/.ssh/id_ecdsa_sk_rk.pub dans ~/.gitconfig
[user]
name = Andrew Helwer
signingKey = "key::sk-ecdsa-sha2-nistp256@openssh.com AAAAInNrLWVjZHNhLXNoYTItbmlzdHAyNTZAb3BlbnNzaC5jb20AAAAIbmlzdHAyNTYAAABBBGxFEdnIg6ppz+pQCdd1eisjOV4gxrjMv1Y4SbtdLoSm6CJCgPZ6q7lnNyuQQsdnS4/Tllsc656AQL7BO3OS47cAAAAEc3NoOg== ssh:"
- Après ce réglage, il a été possible de signer des fichiers avec la clé stockée dans le secure element du MacBook puis de les pousser sur un site GitLab Pages
Résultats sous Windows et Linux
- Un essai rapide a aussi été réalisé sur l’ordinateur portable Windows fourni par l’entreprise
winget install Microsoft.OpenSSH.preview
ssh-keygen -t ecdsa-sk
- Cette commande a elle aussi créé une paire de fichiers clé privée/clé publique, et lors d’une connexion SSH elle acceptait, via le flux standard de connexion Windows Hello, la reconnaissance faciale, l’empreinte ou le PIN
- Sous Linux, il n’a pas été possible de faire une démonstration, faute d’accès à un ordinateur portable dont le secure element demandait une vérification comparable de la présence réelle de l’utilisateur
1 commentaires
Avis sur Lobste.rs
Excellent article, et le simple fait de montrer que c’est possible est déjà très utile
Personnellement, je n’ai pas trouvé la bonne version de bibliothèque pour faire fonctionner ça, mais j’ai découvert que 1Password 8 stocke les clés SSH de façon sécurisée et que son agent prend en charge le déverrouillage de la clé par authentification biométrique
Du coup, je peux maintenant faire du git et me connecter à des hôtes SSH juste en posant mon doigt
Guide : https://developer.1password.com/docs/ssh/get-started/
Ça a l’air réservé à Mac
Je n’ai pas eu l’occasion d’essayer un système Linux avec ce type d’élément sécurisé, et sur ma station de travail Linux, j’ai un TPM V1, mais je ne vois pas vraiment comment garantir que les opérations de signature ne s’exécutent qu’après vérification de la présence réelle de l’utilisateur
Quelqu’un avec un portable Linux comme un Framework pourrait peut-être tenter l’expérience. Peut-être que ça pourrait même fonctionner sur Asahi
Donc, qu’y a-t-il exactement dans le fichier de clé privée fourni ?
ssh:, c’est-à-dire l’application, correspond à l’origin d’une passkey et sert à créer des clés résidentes par hôte ou par domainePar exemple, je m’en sers pour séparer les clés par usage même sur la même Yubikey physique
flagsindique comment le matériel doit traiter la clé [1]. L’agent peut aussi ajouter ses propres restrictionsTechniquement, on peut également stocker d’autres blobs ou extensions dans une clé FIDO ; dans mon précédent poste, je m’en suis servi pour transmettre, avec l’authentification, des identifiants auxiliaires comme une clé publique X.509. C’est assez élégant comme approche
[1]
L’enveloppe externe contient la valeur magique
openssh-key-v1\0,cipher=none,kdf=none, donc ce n’est pas du texte chiffréLe blob de clé publique de 74 octets contient le type de clé
sk-ssh-ed25519@openssh.com, un point Ed25519 de 32 octetsfdcce889…03e7852b, et l’applicationssh:, ce qui sépare les credentials FIDO entre SSH et WebAuthn via un espace de noms distinctLa section privée fait 248 octets et, avec
cipher=none, elle est en clair. On y trouvecheckint1 == checkint2 == 0x46744267comme valeur aléatoire, le type de clé et la clé publique répétés, l’applicationssh:, etflags: 0x01Ce drapeau signifie
USER_PRESENCE_REQUIRED, donc un toucher est requis, mais pas de PIN ni de vérification utilisateur, et il s’agit d’une clé non résidentekey_handleest un ID de credential opaque de 128 octets transmis àauthenticatorGetAssertion, que l’appareil déplie en interne pour retrouver la graine Ed25519Il y a aussi un champ
reservedvide, le commentaireahelwer@ah-mbair.local, et le padding01 02 03Cet article semble ne parler que de SSH, mais y a-t-il un moyen d’utiliser le Secure Enclave ou le TPM de mon ordinateur comme clé FIDO2 ou U2F ?
Une passkey est aussi une forme de cette approche, avec une clé privée distincte pour chaque site web
En voyant ça, je trouve étrange qu’il ne soit pas plus courant de prendre en charge des clés API asymétriques ou HMAC pouvant être liées au matériel
C’est encourageant de voir se multiplier des spécifications comme WebAuthn, DBSC (Device-Bound Session Credentials) et OAuth2 DPOP, qui poussent davantage dans cette direction