8 min de lecture

macOS Keychain pour les outils de programmation autonomes

Découvrez ce que macOS Keychain protège, où cette protection s'arrête et pourquoi un outil autonome exige une exécution authentifiée et déléguée.

macOS Keychain pour les outils de programmation autonomes

macOS Keychain protège bien un identifiant tant que personne ne l'utilise. Cela n'en fait pas une frontière de sécurité complète pour un outil de programmation autonome. Dès qu'un processus peut récupérer un jeton d'API ou une clé privée, il peut en général le copier, l'afficher, l'envoyer ailleurs ou continuer à l'utiliser après la fin supposée de la tâche.

La distinction compte davantage pour un agent que pour une application classique. Une personne choisit une commande et voit le résultat. Un agent interprète du texte, appelle des outils, poursuit à partir de leurs sorties et peut travailler pendant des heures. Le stockage Keychain répond à la question « Qui peut lire ce secret ? ». L'exécution déléguée traite la question plus difficile : « Ce processus précis peut-il effectuer cette action précise maintenant, sans jamais recevoir le secret ? ». L'authentification de session PAM répond à une autre question liée, celle de l'identité qui a ouvert une session. Confondre ces trois contrôles laisse des failles discrètes en démonstration et pénibles à reconstituer après un incident.

Keychain protège le stockage, pas chaque utilisation

Keychain chiffre les données secrètes et régit leur accès par les services de sécurité de macOS. C'est un bien meilleur emplacement pour un jeton qu'un fichier .env, l'historique du shell, un dépôt ou la configuration d'un agent. Un élément peut porter des contrôles d'accès, et le système peut exiger la présence de l'utilisateur avant de le livrer. Ces protections réduisent réellement les lectures non autorisées et le vol banal de fichiers.

La frontière s'arrête à la divulgation. Si un outil appelle un auxiliaire qui renvoie le jeton sous forme de texte, le jeton existe désormais dans l'auxiliaire, sur le trajet IPC et dans le processus destinataire. Il peut se retrouver dans un message de débogage, une exception, une transcription, un rapport de plantage, l'espace d'échange, un environnement copié, un processus enfant ou une requête malveillante. Keychain ne peut pas rappeler ces octets. Verrouiller le trousseau ensuite n'invalide pas un jeton porteur déjà copié en mémoire.

La documentation Keychain Services d'Apple décrit le stockage et la récupération de mots de passe, clés et certificats. Le terme « récupération » est exact : c'est un résultat prévu. Keychain ne transforme pas chaque secret en clé de signature impossible à exporter. Certaines clés cryptographiques peuvent être créées avec des contrôles et utilisées par les API de sécurité sans exporter leur partie privée, mais un jeton d'API ordinaire doit devenir un en-tête HTTP quelque part. Il faut déterminer quel composant de confiance construit et envoie la requête.

Un compte de test suffit à exposer le problème. Stockez une valeur jetable, récupérez-la par le même chemin que l'agent, puis observez ce que le processus peut faire des octets renvoyés :

security find-generic-password -a agent-test -s example-api -w

La sortie contient le secret suivi d'un saut de ligne :

test_token_7f3a...

Si l'agent ou un shell qu'il contrôle exécute cette commande, la couche de stockage a déjà pris sa décision. Masquer la sortie change seulement ce que voit la personne. Le processus peut toujours la rediriger, l'encoder ou l'insérer dans une requête.

Un lecteur autorisé peut exporter le secret

Le principal dangereux n'est souvent pas un inconnu, mais l'outil auquel vous avez donné accès. Pour être utile, un processus autonome doit accéder largement aux fichiers, outils de compilation, commandes de paquets et clients réseau. Si ce même processus lit les identifiants, une injection d'instructions, une dépendance compromise ou une mauvaise commande peut transformer cet accès ordinaire en exportation.

Une exigence de signature peut limiter les applications autorisées à lire un élément Keychain. Elle aide contre un autre processus non signé ou signé autrement. Elle protège beaucoup moins si l'application approuvée est extensible, lance des shells, charge des modules, accepte les consignes non fiables d'un dépôt ou expose un protocole d'outils. Le binaire signé peut précisément exécuter le comportement influencé par l'attaquant.

La délégation pose aussi problème. Un auxiliaire approuvé peut lire un jeton et le remettre à l'agent par l'entrée standard. Keychain voit l'auxiliaire, pas le consommateur final. Si l'auxiliaire place le jeton dans une variable d'environnement, tous les enfants qui héritent de cet environnement peuvent le recevoir. La décision initiale ne précise pas l'opération ultérieure qui était prévue.

Une bonne revue suit le secret comme une donnée, plutôt que comme une case d'un schéma. Elle répond à quatre questions :

  • Quel processus reçoit le texte en clair en premier ?
  • Un enfant, un module, une commande shell ou une transcription peut-il en recevoir une copie ?
  • Le destinataire choisit-il la destination, la méthode HTTP, le chemin ou l'hôte SSH ?
  • Quel événement met fin à son autorité, et efface-t-il ce qui a déjà été divulgué ?

Les équipes répondent souvent à la première question et ignorent les autres. Le système paraît protégé au repos, puis se comporte comme un identifiant durable en clair dès le démarrage de l'agent.

PAM prouve une connexion, pas l'intention de l'agent

Les modules PAM permettent à un service d'appliquer une politique d'authentification à l'ouverture de session. Sous macOS, sudo, login et les chemins de connexion distante utilisent des configurations PAM nommées. Une pile PAM peut authentifier l'utilisateur, vérifier le compte, établir des identifiants et ouvrir ou fermer une session. Elle convient pour décider si une personne peut commencer une session privilégiée du système.

PAM ne régit habituellement pas chaque appel HTTP d'un agent déjà lancé. Après authentification, le processus agit avec l'identité et les capacités reçues du système. S'il peut lire un jeton, PAM ne lie pas le geste Touch ID ou le mot de passe antérieur à une requête ultérieure précise. Sans autre composant, il ne comprend pas non plus que POST /releases est plus sensible que GET /status.

Le mot « session » désigne plusieurs durées indépendantes. Une session PAM peut entourer une connexion ou une activité sudo. Une session de terminal reste dans une fenêtre. Une session d'agent peut être un processus, une conversation ou une tâche reprise. Une session d'API peut durer autant qu'un jeton porteur. Fermer l'une ne ferme pas forcément les autres.

Voici une panne concrète : l'opérateur s'authentifie pour ouvrir un shell, lance l'agent et approuve Keychain. L'agent lit un jeton de déploiement. Des heures plus tard, l'opérateur ferme la conversation visible, mais un enfant reste actif avec le jeton dans son environnement. PAM a correctement authentifié la session et Keychain a correctement autorisé la lecture. Aucun des deux ne contrôlait la durée de l'action, donc aucun n'empêche la suite.

Utilisez PAM pour décider si un compte du système peut établir une session. Son authentification ne prouve pas une intention humaine récente pour chaque action de cette session. PAM n'a pas cette sémantique.

Authentification et autorisation répondent à deux questions

L'authentification établit l'identité d'un principal, ou au moins l'identifiant qu'il présente. L'autorisation décide ce qu'il peut faire dans un contexte. Le stockage conserve la matière secrète jusqu'à une lecture ou une opération cryptographique approuvée. Ces contrôles se complètent sans se remplacer.

Avec un outil autonome, il faut nommer le principal précisément. « L'utilisateur » reste trop vague. L'opérateur, l'exécutable signé, un nouveau processus d'agent, un serveur MCP, un shell enfant et le compte d'API distant sont des principaux différents. Si une approbation les couvre tous, le système doit l'annoncer.

L'unité la plus pratique au quotidien est souvent l'exécution du processus. L'opérateur autorise un processus reconnu jusqu'à sa sortie, puis décide à nouveau pour la prochaine exécution. Un redémarrage silencieux n'hérite donc pas d'une ancienne approbation. Pour un identifiant très sensible, exigez une décision à chaque usage, même dans une exécution autorisée.

Une approbation par appel n'est pas sûre automatiquement. Une boîte indiquant seulement « Autoriser le réseau ? » ne donne presque rien à juger. Elle doit identifier le processus et montrer les détails qui changent le risque : alias, méthode et destination HTTP, ou utilisateur et hôte SSH. Elle doit limiter la fatigue. Si une lecture anodine interrompt autant qu'une écriture en production, les deux seront approuvées sans attention.

L'identité du processus mérite mieux qu'un chemin ou un nom, faciles à copier. Sous macOS, une exigence de signature peut lier la décision à l'autorité de signature et à l'exigence désignée, tandis que l'identifiant du processus distingue les instances. L'opérateur doit voir le fait de signature vérifié, pas un nom convivial qu'un binaire hostile peut imiter.

La signature ne prouve pas le bon comportement. Elle établit la provenance dans son modèle, pas l'intention. Un agent correctement signé peut suivre des consignes hostiles d'un dépôt, et un hôte signé peut charger un contenu contrôlé par l'attaquant. L'approbation signifie donc que ce processus identifié peut demander des actions jusqu'à sa sortie, pas que toutes ses requêtes sont sûres.

Définissez aussi la relation avec les descendants. Faire confiance à tous les enfants crée un arbre difficile à révoquer et donne trop d'autorité à un shell général. Demander une intervention pour chaque auxiliaire éphémère rend l'automatisation inutilisable. Un bon modèle conserve l'autorisation sur la connexion au courtier détenue par l'agent approuvé. Les enfants n'héritent pas des identifiants et n'atteignent les actions protégées que par le canal révoqué avec la session mère.

La sortie du processus forme une fin automatique utile, mais l'opérateur a aussi besoin d'une révocation immédiate. Le courtier doit rejeter les actions en attente et futures, invalider la session et enregistrer la décision. L'arrêt d'une requête déjà envoyée dépend du protocole et du service distant. Révoquer ne rembobine pas un travail achevé.

Testez les redémarrages. Certains clients se mettent à jour, se reconnectent après un plantage ou reprennent une conversation dans un nouveau processus. Le code pratique traite souvent cela comme la même session logique. La frontière de sécurité doit traiter le nouveau processus comme nouveau, sauf transfert délibéré et authentifié. Un identifiant de conversation fourni par l'agent ne prouve rien, car il peut le copier.

On obtient une échelle de décisions, pas une permission démesurée :

  1. Le coffre est-il déverrouillé et disponible ?
  2. Ce nouveau processus peut-il agir pendant sa durée de vie ?
  3. Cet identifiant exige-t-il une décision à chaque usage ?
  4. Un exécuteur de confiance limitera-t-il et enregistrera-t-il l'opération ?

Les trois premières questions gouvernent l'autorité. La quatrième gouverne l'exécution et la preuve. Les fondre dans une option « agent fiable » complique la révocation et l'enquête.

L'exécution déléguée écarte le secret de l'agent

Remplacez les identifiants par MCP
Connectez l'agent par l'adaptateur stdio sans placer de secrets dans ses consignes.

Un courtier remplace l'interface « donne-moi le jeton » par « effectue cette action définie ». L'agent envoie des paramètres non secrets. Le courtier vérifie l'autorité, récupère l'identifiant dans sa propre frontière, l'injecte dans le protocole sortant, exécute l'opération et renvoie seulement le résultat utile.

Une requête d'API HTTP peut prendre cette forme :

{
  "credential": "staging-release-api",
  "method": "POST",
  "url": "https://api.example.invalid/v1/releases",
  "headers": {"content-type": "application/json"},
  "body": {"commit": "8a31c2e", "channel": "candidate"}
}

Après autorisation, le courtier ajoute l'en-tête bearer, basic ou personnalisé. La réponse visible peut conserver le statut, certains en-têtes et le corps, sans l'identifiant injecté :

{
  "status": 201,
  "headers": {"content-type": "application/json"},
  "body": {"release_id": "rel_1042", "state": "queued"}
}

Cette frontière est plus nette qu'un masquage. Le masquage laisse d'abord le secret entrer dans un chemin non fiable, puis tente d'en cacher les formes reconnaissables. La délégation évite la divulgation. Elle rend aussi la révocation effective : quand le courtier refuse la requête suivante, l'agent n'a pas de jeton en cache pour le contourner.

Les sorties demandent tout de même des précautions. Une API peut renvoyer les en-têtes reçus, une commande SSH afficher l'environnement et un client verbeux inclure l'authentification dans une erreur. Filtrez les emplacements connus avant de répondre, limitez les diagnostics et testez les échecs autant que les succès. « L'agent ne reçoit jamais l'identifiant » doit couvrir erreurs et journaux.

SSH suit la même idée avec un exécuteur qui comprend le protocole. L'agent demande une commande sur une identité d'hôte nommée. L'exécuteur utilise la clé privée en interne, vérifie l'hôte, lance la commande et renvoie stdout, stderr et le statut. Donner un fichier de clé temporaire à l'agent reste une divulgation, même avec nettoyage ultérieur.

Un courtier ne bloque pas toute action autorisée nuisible

Garder le jeton hors de l'agent retire une grande classe de pannes, mais ne rend pas l'action correcte. Une requête DELETE autorisée peut effacer des données sans fuite de secret. Une commande SSH peut abîmer un hôte alors que la clé reste protégée. Le courtier contrôle l'exercice de l'autorité, pas l'intention métier cachée derrière la syntaxe.

La destination compte. Si l'agent choisit toute URL et que le courtier ajoute aveuglément l'identifiant, il peut l'envoyer à l'hôte d'un attaquant. Un exécuteur HTTP sérieux lie chaque identifiant aux bonnes destinations ou garantit autrement que l'authentification n'est injectée qu'au bon endroit. Les redirections exigent la même attention. Pour SSH, la vérification de l'hôte fait partie de l'exécution et ne doit pas dépendre d'une option de l'agent.

Les données de réponse restent exposées. Un identifiant bien protégé peut autoriser une réponse contenant dossiers clients, configuration ou autre secret. L'agent ne devrait recevoir que le minimum utile, mais les API génériques rendent cette limite difficile. La délégation réduit l'exposition des identifiants, pas automatiquement les fuites de données.

L'injection d'instructions reste possible. Le texte d'un dépôt, les commentaires, la sortie d'une compilation et la documentation récupérée peuvent pousser l'agent vers une action valide mais indésirable. Une approbation humaine n'aide que si la carte est détaillée et lue. Les systèmes sensibles peuvent exiger des identifiants distants plus limités, des portées côté API, des branches protégées, un environnement de test, une liste de commandes ou un moteur de workflow distinct.

Le conseil populaire « mettez chaque secret dans Keychain et exigez Touch ID » combine un bon stockage avec une décision d'usage trop large. Touch ID peut prouver une présence lors de la livraison, pas empêcher le destinataire de copier ou détourner ensuite. Demandez la biométrie à la frontière de l'action quand elle le mérite, sans livrer le jeton à l'agent.

La preuve d'audit doit décrire les actions et résister aux retouches

Protégez les clés par Touch ID
Marquez une clé pour exiger une nouvelle décision à chaque usage.

Un enregistrement utile indique qui a demandé l'action, quelle instance l'a demandée, quel alias a été choisi, quelle destination et quelle opération ont été utilisées, quelle autorisation a eu lieu, quand l'action a tourné et comment elle a fini. Le secret lui-même en est absent. Une transcription de console suffit rarement : elle mélange texte du modèle, messages d'outils, commandes et sorties tronquées sans modèle d'événement stable.

Séparez les événements de session et d'appel tout en gardant leur lien. Le journal de sessions montre l'apparition du processus, son identification, son approbation, sa révocation et sa sortie. Le journal d'activité montre chaque opération HTTP ou SSH et son résultat. Un enquêteur peut répondre à la fois à « Cette exécution était-elle autorisée ? » et « Qu'a-t-elle fait ? ».

Un journal chiffré protège la confidentialité au repos, mais ne prouve pas l'absence de suppression ou de réordonnancement. Une chaîne de hachage lie chaque entrée à la précédente afin de détecter une entrée chiffrée modifiée, manquante ou déplacée. Elle ne prouve pas que chaque événement a été enregistré et n'empêche pas la suppression du magasin entier. Elle fournit une preuve d'intégrité des données restantes, rien de plus.

Une commande de vérification doit donner un résultat simple et automatisable. Sallyport projette ses journaux Sessions et Activity depuis un journal d'audit chiffré, chaîné par hachage et sans lecture en retour. Son vérificateur travaille hors ligne sur le texte chiffré sans clé de déchiffrement :

sp audit verify

Un succès doit indiquer la chaîne vérifiée et rendre zéro; un lien rompu doit signaler l'échec et rendre un autre statut. Placez ce contrôle dans la collecte d'incident et la vérification des sauvegardes. Une icône verte affichée par la même application modifiable n'apporte pas une preuve indépendante.

Les journaux ont aussi besoin d'un contrat de masquage. Enregistrez les alias, pas les valeurs. Décidez si les corps et sorties sont stockés, tronqués, hachés ou exclus. Testez les requêtes mal formées et erreurs de transport, souvent plus bavardes que les succès.

Évaluez tout le chemin avec des tests adverses

Révoquez immédiatement l'agent actif
Le journal Sessions coupe les actions futures sans poursuivre des jetons copiés.

Une promesse d'architecture prend du sens lorsqu'un test tente de la violer. Créez un identifiant et un point de terminaison jetables, puis exercez les mêmes binaires et frontières que le vrai travail. Une capture d'une boîte de permission ne prouve pas ce qui atteint la mémoire ou reste chez les descendants.

Un plan compact couvre les limites importantes :

  1. Lancez un nouveau processus et vérifiez que l'ancienne approbation ne suit pas. Notez l'identité affichée.
  2. Demandez une lecture à faible risque, puis une écriture protégée par appel. La carte doit distinguer destination et opération.
  3. Recherchez le secret et ses variantes encodées dans stdout, stderr, environnement, transcriptions, charges d'outils et rapports de plantage visibles.
  4. Révoquez la session, puis réessayez depuis le parent et un enfant existant. Les deux doivent échouer avant le réseau ou SSH.
  5. Modifiez, retirez et réordonnez des copies du journal. La vérification hors ligne doit échouer pour chaque chaîne corrompue.

Ajoutez deux abus souvent oubliés. Faites rediriger le point de test vers une autre origine et vérifiez que l'authentification ne suit pas. Faites-lui renvoyer tous les en-têtes puis échouer avec une erreur détaillée; le courtier doit retirer le secret avant la réponse à l'agent.

Pour SSH, essayez une clé d'hôte inconnue, une clé changée, une demande de mot de passe interactive et une commande qui affiche son environnement. Déterminez qui possède chaque décision. Un auxiliaire sans état ne doit ni créer un second cache ni hériter d'un environnement trop large.

Les critères doivent décrire des résultats observables. « Secret protégé » n'est pas testable. « La séquence d'octets et sa forme base64 n'apparaissent jamais dans les fichiers, environnements, réponses ou journaux visibles de l'agent » l'est. « La révocation fonctionne » reste vague. « Chaque appel du processus révoqué et de ses enfants échoue avant l'ouverture d'une socket » se mesure.

Choisissez les contrôles selon l'autorité reçue

Keychain seul peut suffire quand une application conventionnelle récupère un identifiant peu puissant, suit un chemin étroit, reçoit chaque opération directement de l'utilisateur et peut connaître le secret selon le modèle de menace. Il reste une bonne couche dans un système plus fort. Remplacer un magasin chiffré mûr par un fichier maison n'apporte rien.

La réponse change si l'outil choisit les opérations, exécute du code non fiable, crée des enfants arbitraires ou continue sans surveillance. Donnez à l'agent des capacités d'action, pas les octets d'un identifiant. Authentifiez chaque nouveau processus, réservez la confirmation par appel aux clés sensibles, liez les identifiants aux protocoles et destinations, et rendez la révocation immédiate pour les usages futurs.

Avant de choisir, écrivez la promesse la plus forte à tenir. Le chiffrement sur disque appelle un contrôle de stockage; limiter les opérations à ce processus signé exige identité et session; appeler une API sans obtenir son jeton exige un courtier; détecter une retouche des journaux restants exige une preuve d'intégrité. Chaque promesse a son test et son responsable.

Notez aussi le risque restant. Un courtier n'annule pas une écriture distante, une session ne retire pas les données déjà rendues, la signature ne prouve pas des consignes inoffensives et une chaîne de hachage ne prouve pas la conservation du magasin entier. La revue cesse ainsi de créditer un contrôle pour un travail qu'il ne fait pas.

La migration peut être progressive. Commencez par les jetons de déploiement, clés d'infrastructure et identités SSH qui donnent le plus d'autorité. Remplacez leur interface de lecture par de petites actions, supprimez les anciennes variables et gardez un identifiant de test jusqu'à ce que les vieux chemins échouent.

Attribuez enfin les responsabilités de déverrouillage, approbation, révocation, rotation et vérification. Si un agent se comporte mal, révoquez d'abord sa session, préservez et vérifiez l'audit, puis renouvelez tout identifiant qui a pu atteindre sa mémoire.

Sallyport applique ce modèle sous macOS : les agents passent par son serveur sp mcp pour les actions HTTP et SSH, tandis que les clés restent dans son coffre chiffré en processus. Son échelle fixe comprend une porte absolue du coffre, une autorisation par défaut pour chaque nouveau processus et une approbation facultative à chaque usage d'une clé choisie.

Ce modèle n'est ni un moteur général de politiques ni un proxy d'interception. Un courtier étroit peut faire des promesses fortes sur la garde des identifiants parce qu'il possède des chemins d'exécution définis. Les actions extérieures ont besoin de leurs propres contrôles, et les services distants doivent encore imposer les portées, séparer les environnements et révoquer les comptes.

La question décisive est concrète : après approbation, le processus peut-il afficher, copier ou réutiliser seul l'identifiant ? Si oui, Keychain a protégé le stockage, pas contenu l'usage. Gardez-le pour ce qu'il fait bien et déplacez la frontière d'autorité vers l'exécuteur qui effectue et enregistre l'action.

FAQ

macOS Keychain est-il sûr pour les clés d'API ?

Oui, c'est un magasin chiffré raisonnable sur Mac. Le risque change quand un processus autonome récupère le clair et peut le copier ou le réutiliser hors de la tâche.

Un agent peut-il lire les mots de passe de Keychain ?

Oui, si son processus ou son auxiliaire satisfait les contrôles et que l'utilisateur approuve l'invite requise. Après livraison, Keychain ne contrôle plus le destinataire.

Touch ID empêche-t-il une fuite par l'agent ?

Touch ID autorise un accès à un instant. Il n'empêche pas le processus approuvé de conserver, divulguer ou détourner le texte reçu ensuite.

Quelle différence entre PAM et Keychain ?

PAM authentifie les utilisateurs et ouvre des sessions système. Keychain stocke les secrets et régit leur accès; aucun n'autorise automatiquement chaque action distante.

Que signifie l'exécution déléguée des identifiants ?

L'agent demande une action HTTP ou SSH à un exécuteur de confiance plutôt que le secret. L'exécuteur l'injecte en interne et renvoie le résultat.

Un courtier peut-il empêcher les commandes destructrices ?

Pas par la seule dissimulation des clés. Il doit limiter destinations ou commandes, le service doit imposer des portées et les actions sensibles peuvent exiger une approbation.

Chaque action doit-elle demander Touch ID ?

En général, non. Une même invite pour lecture anodine et écriture dangereuse fatigue; utilisez la session au quotidien et le contrôle par usage pour les clés sensibles.

Comment révoquer l'accès d'un agent autonome ?

Révoquez la session active et désactivez ou renouvelez l'identifiant distant si le clair a pu sortir. Fermer la conversation ne suffit pas si des enfants ou copies survivent.

Que doit contenir le journal d'un agent ?

Identité du processus, décision de session, alias, destination, opération, autorisation, résultat et heure. Excluez les valeurs secrètes et fixez le traitement des corps et sorties.

Quand Keychain seul suffit-il ?

Il peut suffire pour une application étroite pilotée par l'utilisateur si la divulgation est acceptable et le compte distant limité. Ce n'est pas une bonne frontière finale pour un agent autonome.

Sallyport

Sallyport exécute les appels d'API et les commandes SSH à la place de votre agent IA. Les clés restent dans un coffre-fort local sur votre Mac ; vous approuvez chaque exécution et chaque action est consignée dans un journal scellé.

© 2026 Sallyport · Open source sous Apache-2.0 · Oleg Sotnikov