Darkwood Blog Blog
  • Articles
  • Veille
  • Releases
  • CrĂ©ateurs
fr
  • de
  • en
Connexion
  • Blog
  • Articles
  • Veille
  • Releases
  • CrĂ©ateurs

🔑 Les clĂ©s secrĂštes deviennent des donnĂ©es d'application

le 20 septembre 2026

Connectez-vous pour réagir à cet article

🚀 1

🔑 Les clĂ©s secrĂštes deviennent des donnĂ©es d'application

La plupart des applications Symfony que j’écris considĂšrent encore la clĂ© du modĂšle comme faisant partie de l’infrastructure :

ANTHROPIC_API_KEY=...

Cette ligne est simple car la propriĂ©tĂ© est simple. L’identifiant appartient au dĂ©ploiement. Les opĂ©rateurs l’enregistrent dans .env.local ou dans un coffre-fort de secrets. Le conteneur l’injecte une seule fois au dĂ©marrage. L’application n’a jamais Ă  l’accepter, Ă  le stocker ou Ă  dĂ©cider quand il est autorisĂ© Ă  exister en mĂ©moire.

Le modÚle « Bring-your-own-key » (apportez votre propre clé) change le propriétaire.

Que se passe-t-il lorsque la clĂ© appartient Ă  l’utilisateur Ă  la place ?

Elle arrive via HTTP. Elle est associĂ©e Ă  une entitĂ© de l’application. Elle doit perdurer d’une requĂȘte Ă  l’autre. Elle ne doit pas ĂȘtre stockĂ©e en clair, ne doit pas ĂȘtre rĂ©insĂ©rĂ©e dans le code HTML, et ne doit ĂȘtre dĂ©chiffrĂ©e que lorsqu’un Ă©lĂ©ment a rĂ©ellement besoin d’appeler un fournisseur. Ce fournisseur peut engager des dĂ©penses.

Le fichier .env n’est plus la solution. L’identifiant est devenu une donnĂ©e d’application.

L’expĂ©rience de cette semaine, navi-key-management, est une petite application Symfony 8.2 qui implĂ©mente ce cycle de vie puis le mesure. Je rapporte ici ce qui a Ă©tĂ© exĂ©cutĂ© le 19/09/2026 : PHP 8.5.4, Symfony 8.2.x-dev, SQLite, Sodium local. Symfony KeyManagement est expĂ©rimental. C’est ce qu’indiquent ses docblocks d’installation. ConsidĂ©rez chaque signature ici comme un instantanĂ©, et non comme une promesse concernant la version stable 8.2.

BYOK modifie les limites

Le chemin que j’ai codĂ© est celui que je souhaitais pouvoir lire :

Utilisateur
  ↓
Formulaire Symfony
  ↓
Chiffrement par KeyManagement
  ↓
Texte chiffré SQLite
  ↓
DĂ©chiffrement au moment de l’exĂ©cution
  ↓
Darkwood Navi
  ↓
Symfony AI
  ↓
Anthropic / fournisseur

Chaque flĂšche reprĂ©sente un type de traitement diffĂ©rent. Le formulaire ne sait pas comment une requĂȘte de chat est construite. Navi ne stocke pas les secrets. Symfony AI ne dĂ©tient pas la ligne de la base de donnĂ©es. KeyManagement ne communique pas avec Claude.

Je n’ai pas ajoutĂ© de couche de rĂ©fĂ©rentiel ni de deuxiĂšme SDK d’IA. Les abstractions intĂ©ressantes existaient dĂ©jĂ  dans la pile. L’une d’entre elles pouvait facilement ĂȘtre mal nommĂ©e.

darkwood/navi est le shell du workflow : Action, Context, WorkflowRunner, Event. Il ne comporte ni client Anthropic ni type pour une clĂ© API. C’est Symfony AI qui communique avec le modĂšle. La chaĂźne d’exĂ©cution dans ce rĂ©fĂ©rentiel est la suivante :

AiConnection
    ↓
CredentialProtector::decrypt()
    ↓
WorkflowRunner
    ↓
TestAiConnectionAction
    ↓
Anthropic\Factory::createPlatform($apiKey)
    ↓
provider

Navi orchestre une étape. Il ne remplace pas Symfony AI.

Il y a eu un problĂšme de compatibilitĂ© des paquets qui mĂ©rite d’ĂȘtre mentionnĂ© ici, car il est bien rĂ©el. Le paquet Packagist darkwood/navi ^1.0 nĂ©cessite toujours Symfony 8.0. Cette application a besoin de la version 8.2.x-dev ; Navi est donc un dĂ©pĂŽt de chemin d’accĂšs vers le monorepo local (8.1.x-dev). ExpĂ©rimenter avant que les contraintes de la version finale ne soient fixĂ©es, c'est ça : un lien symbolique, pas un slogan.

Symfony se dote d'une primitive de gestion des clés

Une fois que la clé est considérée comme une donnée d'application, la premiÚre piÚce manquante n'est pas un autre wrapper AI. C'est un moyen de chiffrer un petit secret sans avoir à inventer un service de cryptographie.

KeyManagement de Symfony 8.2 est cette fonctionnalitĂ© de base. J’ai installĂ© symfony/key-management et symfony/aws-key-management sur la version 8.2.x-dev et j’ai utilisĂ© les interfaces que le conteneur connecte automatiquement :

$ciphertext = $this->encrypter->encrypt($this->keyId, $apiKey);
$connection->setCiphertextBlob(base64_encode($ciphertext->blob));
$connection->setKmsKeyId($ciphertext->keyId);

La mĂ©thode encrypt() renvoie un objet Ciphertext, et non une chaĂźne de caractĂšres. La ligne regroupe le blob et l’identifiant de clĂ©. L’application injecte les interfaces EncrypterInterface et DecrypterInterface. Le chiffrement par enveloppe est disponible dans le mĂȘme bundle. Je ne l’ai pas utilisĂ©. Une clĂ© API fait plusieurs centaines d’octets, ce qui est bien en deçà de la limite de 4 Ko imposĂ©e par le chiffrement direct sur AWS KMS. Le chiffrement d’enveloppe est destinĂ© aux fichiers et aux documents volumineux, ou Ă  une politique stipulant que le KMS ne doit jamais avoir accĂšs au texte en clair de l’application. Ce n’est pas obligatoire, car le composant le fournit.

La configuration que j’ai finalement retenue n’est pas le DSN d’une seule ligne que j’avais d’abord essayĂ©.

key_management:
    clients:
 app: 'sodium://?keys[app-key]=%env(DEV_KMS_KEY)%'
    default_client: app

when@prod:
    key_management:
 clients:
 app: 'aws-kms://default?region=%env(AWS_KMS_REGION)%'

Un simple '%env(KMS_DSN)%' ne se compile pas. L’arborescence de configuration expĂ©rimentale marque le DSN du client comme cannotBeEmpty, et une variable d’environnement entiĂšrement substituĂ©e est traitĂ©e comme vide au moment de la compilation :

Le chemin « key_management.clients.app » ne peut pas contenir de variable d’environnement
lorsque les valeurs vides ne sont pas autorisĂ©es par dĂ©finition et font l’objet d’une validation.

Le modĂšle de fonctionnement conserve un schĂ©ma littĂ©ral — sodium:// dans dev/test, aws-kms:// dans prod — et ne remplace par la variable d’environnement que la clĂ© ou la rĂ©gion.

dev / test
    ↓
Sodium ← testĂ© dans ce dĂ©pĂŽt

prod
    ↓
AWS KMS ← configurĂ©, non appelĂ©

C’est grĂące Ă  Sodium que l’expĂ©rience est reproductible localement. Le processus dispose de DEV_KMS_KEY. Je l’ai gĂ©nĂ©rĂ©e avec sodium_crypto_aead_xchacha20poly1305_ietf_keygen() et Base64UrlSafe. Quiconque peut lire cette valeur d’environnement peut dĂ©chiffrer toutes les clĂ©s API stockĂ©es. C’est acceptable pour une dĂ©monstration sur un ordinateur portable. Ce n’est pas le cas en production.

AWS KMS est la frontiĂšre externe vers laquelle pointe le DSN de production. Le pont AWS chiffrer cĂŽtĂ© serveur ; la clĂ© principale est censĂ©e rester dans KMS. Je n’ai pas envoyĂ© de requĂȘte Ă  AWS. Le bloc de production correspond Ă  la configuration, et non Ă  un rĂ©sultat de test.

Le badge de l’interface utilisateur n’analyse pas le DSN. KMS_BACKEND est une Ă©tiquette (Sodium ou AWS KMS), de sorte que le matĂ©riel de chiffrement ne devient jamais une variable Twig.

La clĂ© de dĂ©veloppement enregistrĂ©e dans le fichier .env de l’expĂ©rience est une clĂ© de dĂ©veloppement uniquement. GĂ©nĂ©rez une clĂ© privĂ©e Sodium avant d’utiliser ce modĂšle pour une application rĂ©elle.

Une petite expérience

Le modĂšle est constituĂ© d’une seule entitĂ©, AiConnection : libellĂ©, fournisseur, modĂšle, point de terminaison facultatif, ciphertextBlob, kmsKeyId, horodatages. Il n’y a pas de colonne contenant la clĂ© API en clair.

L’interface utilisateur se compose de quatre routes et d’un panneau sombre. Elle a pour but de rendre le cycle de vie visible : une liste avec des badges « chiffrĂ© » et « backend », un formulaire, une action de test, une page de rĂ©sultats. Il ne s’agit pas d’un produit de type tableau de bord.

GET /
GET|POST   /connections/new
GET|POST   /connections/{id}/edit
POST /connections/{id}/test

Cela constitue l'intégralité de l'interface de l'application.

Le formulaire est désormais une frontiÚre de sécurité

La clĂ© Ă©tait auparavant introduite via dotenv. Elle est dĂ©sormais saisie via un formulaire. Le formulaire fait donc dĂ©sormais partie intĂ©grante de la conception de la sĂ©curitĂ©, et n’est plus simplement une « enveloppe » d’un DTO.

Symfony 8.2 peut dĂ©duire le type de formulaire Ă  partir de la classe qui contient les donnĂ©es. Ce DTO comporte cinq champs. C’est Ă  cette taille que les nouveaux attributs sont adaptĂ©s.

#[AsFormType]
final class AiConnectionInput
{
    #[FormField(ChoiceType::class, [
 'choices' => [
 'Anthropic' => 'anthropic',
 'OpenAI' => 'openai',
 ],
    ])]
    public string $provider = 'anthropic';

 #[FormField(PasswordType::class, [
 'always_empty' => true,
 'attr' => ['autocomplete' => 'new-password'],
    ])]
    #[Assert\NotBlank(groups: ['create'])]
    public ?string $apiKey = null;
}

createForm(AiConnectionInput::class, $input) suffit. Il n'y a pas d'AbstractType. Le nom compilé est ai_connection_input.

Le comportement de sĂ©curitĂ© que je souhaitais est assurĂ© Ă  la fois par les attributs et par le contrĂŽleur. Le champ « key » correspond Ă  la saisie d’un mot de passe. always_empty empĂȘche le navigateur d’afficher une valeur. Lors de la modification, le contrĂŽleur copie le libellĂ©, le fournisseur, le modĂšle et le point de terminaison dans le DTO et laisse apiKey nul. Un envoi sans valeur n’appelle pas encrypt() ; le texte chiffrĂ© existant est conservĂ©. NotBlank n’apparaĂźt que dans le groupe create.

Les formulaires basĂ©s sur des attributs m’ont Ă©vitĂ© d’écrire une classe que j’aurais autrement dĂ» crĂ©er. Ils n’ont pas supprimĂ© les groupes de validation ni la protection CSRF. La recette Flex « stateless-CSRF » a laissĂ© value="csrf-token" dans le code HTML et prĂ©voyait un contrĂŽleur Stimulus que cette application ne fournit pas. La protection CSRF par session a permis de faire fonctionner un envoi normal via le navigateur. C’est une petite leçon de la version 8.2 qui vient complĂ©ter la grande : les nouveaux attributs de formulaire s’intĂšgrent bien dans un DTO de configuration, et ils ne suppriment pas le reste de la pile de formulaires.

Texte chiffré dans SQLite

AprĂšs l’envoi par le navigateur d’une fausse clĂ©, SQLite a conservĂ© des mĂ©tadonnĂ©es ainsi qu’un blob :

$ sqlite3 var/data.db "SELECT kms_key_id, length(ciphertext_blob), instr(ciphertext_blob, 'sk-ant') FROM ai_connection;"
app-key|96|0

instr(ciphertext_blob, 'sk-ant') = 0. Une comparaison LIKE avec la valeur soumise a également donné 0. Le texte stocké est un Base64 opaque de Ciphertext::$blob.

Il s’agit de la preuve de persistance la plus solide de la semaine, mais elle prĂ©sente une limite claire. Elle prouve que le texte en clair identifiable est absent de cette colonne. Il ne s’agit pas d’un audit cryptographique. Elle ne prouve pas que l’algorithme de chiffrement soit Sodium XChaCha, ne prouve pas la gestion des nonces, et ne prouve pas qu’un attaquant disposant de la clĂ© principale de dĂ©veloppement soit bloquĂ©. PHPUnit effectue le mĂȘme contrĂŽle d’absence sur la rĂ©ponse HTTP : la clĂ© que j’ai publiĂ©e n’apparaĂźt pas dans le code HTML.

La page de liste affiche chiffrĂ© et Sodium. Elle n’affiche pas la clĂ©.

Déchiffrer au dernier moment responsable

La consultation d’une connexion ne dĂ©chiffre rien. Sa modification ne dĂ©chiffre rien. La clĂ© n’est reconstruite que lorsqu’une action d’IA en a besoin.

$apiKey = $this->credentials->decrypt($connection);

try {
    $result = $this->workflows->run(
 Context::fromArray([
 'connection_id' => $connection->getId(),
 'provider' => $connection->getProvider(),
 'model' => $connection->getModel(),
 ]),
        [new TestAiConnectionAction($apiKey, /* fournisseur, modĂšle, point de terminaison */)],
    );
} finally {
    $apiKey = str_repeat("\0", \strlen($apiKey));
    unset($apiKey);
}
texte chiffré stocké
 ↓
Test de connexion / sondage
 ↓
déchiffrement
 ↓
constructeur de TestAiConnectionAction
 ↓
Factory::createPlatform($apiKey)
 ↓
une invite

L'action marque l'argument du constructeur #[\SensitiveParameter]. Le Context transporte des identifiants. Il ne transporte pas le secret. C’est dĂ©libĂ©rĂ© : le Context de Navi dispose de la mĂ©thode toArray(). Y placer la clĂ© transformerait l’état de l’orchestration en une fuite d’informations.

app:probe-connection a signalĂ© context_has_secret: no aprĂšs une exĂ©cution en production. Navi peut orchestrer l’appel sans intĂ©grer les identifiants dans le flux de travail.

Au sein de l’action, Symfony AI est instanciĂ© au moment de l’exĂ©cution. Une variable ANTHROPIC_API_KEY au niveau du conteneur dans ai.yaml aurait contrecarrĂ© le principe BYOK.

AnthropicFactory::createPlatform(
    $this->apiKey,
    $this->httpClient,
    baseUrl: $this->endpoint ?: 'https://api.anthropic.com',
);

La prompt ne comporte qu’une seule ligne : RĂ©ponds par le mot pong. Si le client HTTP renvoie un message contenant la clĂ©, SecretRedactor remplace cette sous-chaĂźne avant que l’erreur n’atteigne Twig.

Une requĂȘte ayant Ă©chouĂ© peut tout de mĂȘme nous apporter des informations

J’ai utilisĂ© un identifiant Anthropic disponible localement via app:probe-connection. Je ne vais pas l’afficher ici.

plaintext_in_ciphertext: no
decrypt_matches: yes
context_has_secret: no
test_ok: false
test_error: Votre solde de crĂ©dit est trop faible pour accĂ©der Ă  l’API Anthropic. ...

Le chiffrement et le dĂ©chiffrement ont concordĂ©. La clĂ© Ă©tait absente de Navi Context. La requĂȘte a quittĂ© le processus et a atteint Anthropic. Anthropic a rĂ©pondu par une erreur de facturation.

Une erreur de facturation reste une preuve de la distance parcourue par la requĂȘte. Une rĂ©ponse indiquant une clĂ© invalide aurait signifiĂ© que nous ne nous Ă©tions jamais authentifiĂ©s. Nous sommes allĂ©s plus loin que cela.

Il ne s’agit pas d’une opĂ©ration rĂ©ussie. Il ne s’agit pas d’une utilisation de jeton. Il ne s’agit pas d’un reçu. Le parcours de l’interface utilisateur avec une fausse clĂ© affichait « La clĂ© API est invalide. » et aucune information confidentielle. Les deux parcours d’erreur Ă©taient utiles. Aucun des deux ne correspond Ă  une conversation aboutie.

Les clĂ©s ont un rayon d’impact Ă©conomique

L’article « Cost of a diff » de Guillaume Moigneu est Ă  l’origine d’une idĂ©e, et non de ce code : une action d’IA a un coĂ»t Ă©conomique observable. Des jetons d’entrĂ©e, des jetons de sortie, des jetons mis en cache, un modĂšle, un tarif.

Un secret qui dĂ©verrouille un fournisseur d’IA ne sert donc pas uniquement au contrĂŽle d’accĂšs. Il protĂšge quelque chose qui peut dĂ©penser de l’argent.

L’application dispose du chemin comptable. TestAiConnectionAction lit l’interface TokenUsageInterface de Symfony AI lorsque le rĂ©sultat de l’appel contient token_usage :

jetons de prompt → entrĂ©e
jetons de complĂ©tion    → sortie
crĂ©ation de cache → cache_write
lecture de cache → cache_read
jetons mis en cache → mis en cache

Les mĂ©triques manquantes restent null et s’affichent comme « non rapportĂ©es ». AiCostTable applique ensuite une liste explicite par million de dollars US datĂ©e du 19/09/2026 :

usd = entrĂ©e  × taux_d’entrĂ©e  / 1_000_000
    + sortie × taux_de_sortie / 1_000_000
    + cache_write × cache_write_rate / 1_000_000   (si la table en comporte un)
    + cache_read  × cache_read_rate  / 1_000_000   (si la table en comporte un)

Claude Sonnet 4.5 : dans cette table, les valeurs sont respectivement de 3 $ / 15 $ / 3,75 $ / 0,30 $. Une simulation locale avec des chiffres fictifs (42 entrĂ©es, 8 sorties) donne 0,000246 $. Ce chiffre est issu d’une vĂ©rification sur tableur. Il ne s’agit pas d’un reçu du fournisseur.

Aucune transaction payante n’a abouti cette semaine. Le chemin de facturation existe. Il n’a pas Ă©tĂ© validĂ© par rapport aux mĂ©tadonnĂ©es d’utilisation fournies par Anthropic. Je ne vais pas inventer de jetons pour donner l’impression que l’interface utilisateur est terminĂ©e.

Le fournisseur n’est pas un point de terminaison

Une discussion sur Symfony AI cette semaine a indiquĂ© que Claude, via un abonnement Azure AI, fonctionnait en redirigeant le pont Anthropic existant vers une URL de messages compatible avec Anthropic, plutĂŽt qu’en ajoutant une nouvelle abstraction Azure-Claude.

La structure rapportée était la suivante :

<resource>.services.ai.azure.com/anthropic/v1/messages

Le test rapporté comprenait un échange de messages complet et la mise en cache des invites. Je ne répéterai pas les identités privées mentionnées dans ce fil de discussion.

La mĂ©thode Factory::createPlatform() d’Anthropic dĂ©jĂ  installĂ©e dispose dĂ©jĂ  de $baseUrl. ModelClient envoie des requĂȘtes vers {baseUrl}/v1/messages. Le point de terminaison facultatif du formulaire correspond donc Ă  la base Azure sans ce suffixe :

https://<resource>.services.ai.azure.com/anthropic

baseUrl est configurĂ©e. L’appel Ă  Azure Anthropic n’a pas Ă©tĂ© effectuĂ©.

Bedrock ne fonctionne pas de la mĂȘme maniĂšre. Il s’authentifie Ă  l’aide d’identifiants AWS, et non d’une clĂ© Anthropic associĂ©e Ă  un hĂŽte. La compatibilitĂ© Ă©tait un problĂšme de point de terminaison pour la passerelle Azure. Il s’agit d’un problĂšme de modĂšle d’authentification pour Bedrock. Je n’ai pas ajoutĂ© de couche Bedrock.

L’identitĂ© du fournisseur, le point de terminaison de transport et le stockage des identifiants sont des prĂ©occupations distinctes.

Parfois, la compatibilitĂ© est une question de configuration, et non une autre abstraction. C’est la mĂȘme habitude que de garder Navi hors de la persistance et Symfony AI hors du formulaire.

Sodium en local, KMS en externe

Je ne vais pas transformer cet article en tutoriel de cryptographie. La différence opérationnelle suffit.

Avec Sodium, l’application dĂ©tient la clĂ© de chiffrement et peut dĂ©chiffrer chaque ligne qu’elle a Ă©crite. L’expĂ©rience est exĂ©cutable sans compte AWS. La compromission de DEV_KMS_KEY Ă©quivaut Ă  la compromission des clĂ©s API stockĂ©es.

Avec AWS KMS, l’application interroge un service externe. La clĂ© principale est censĂ©e rester en dehors du stockage de l’application. La compromission du fichier .env ne suffit pas ; un attaquant a toujours besoin des identifiants AWS et d’une politique de clĂ© autorisant le dĂ©chiffrement.

Il s’agit d’une amĂ©lioration en matiĂšre de gestion des clĂ©s. Ce n’est pas une protection miracle contre un environnement d’exĂ©cution compromis. Si l’application en cours d’exĂ©cution peut appeler decrypt(), un attaquant suffisamment prĂ©sent peut Ă©galement l’appeler. L’avantage rĂ©side dans le fait que la clĂ© principale n’est pas une chaĂźne d’octets placĂ©e Ă  cĂŽtĂ© du texte chiffrĂ©, et que le chiffrement et le dĂ©chiffrement peuvent ĂȘtre auditĂ©s en dehors de l’application. J’ai vĂ©rifiĂ© le premier cas de figure. J’ai configurĂ© le second.

L’IA modifie les deux facettes de la sĂ©curitĂ©

Fabien Potencier a Ă©crit ce mois-ci que Symfony recevait auparavant environ un rapport de sĂ©curitĂ© par trimestre et qu’il en reçoit dĂ©sormais au moins un par jour. Les agents analysent une nouvelle version dĂšs sa sortie. Un rapport arrivant dans la boĂźte de rĂ©ception doit ĂȘtre considĂ©rĂ© comme dĂ©jĂ  public. Les embargos partaient du principe que la dĂ©couverte d’une faille coĂ»tait cher.

Il ne s’agit pas d’un CVE dans cette application. Je n’associe pas cette expĂ©rience Ă  un avis numĂ©rotĂ©.

Le lien pertinent est d’ordre Ă©conomique. Les agents rĂ©duisent le coĂ»t de la recherche, dans une base de code, de l’endroit oĂč un secret fuit. ParallĂšlement, un identifiant gĂ©nĂ©rĂ© par l’IA a une valeur financiĂšre directe : il peut ĂȘtre dĂ©pensĂ©. Ces deux faits combinĂ©s expliquent pourquoi la gestion BYOK (Bring Your Own Key) relĂšve de l’architecture de l’application plutĂŽt que de la gestion du dĂ©ploiement.

Les choix concrets dans le dĂ©pĂŽt sont volontairement simples : pas de colonne en texte clair, pas de saisie de mot de passe, pas de rĂ©affichage, dĂ©cryptage uniquement pour l’exĂ©cution, clĂ© absente de Navi Context, faux fichier .env.example, badge backend sans DSN. KeyManagement se charge du chiffrement. Le reste relĂšve des bonnes pratiques de sĂ©curitĂ©, car l’objet que nous protĂ©geons est dĂ©sormais une ligne de base de donnĂ©es.

Ce qui a réellement fonctionné

Ces vérifications ont été effectuées.

Container, Twig et YAML ont passé les vérifications de syntaxe sans erreur.

PHPUnit : 2 tests, 15 assertions, OK. Un test effectue un chiffrement et un dĂ©chiffrement via Sodium et vĂ©rifie que le texte en clair est absent du blob de l’entitĂ©. L’autre envoie le formulaire basĂ© sur les attributs et vĂ©rifie que la clĂ© est absente de SQLite et du code HTML.

Dans le navigateur, j’ai ajoutĂ© une connexion, j’ai vu les badges « cryptĂ© » et « Sodium », j’ai ouvert l’édition avec un champ de clĂ© API vide, puis j’ai testĂ© un faux identifiant. Le fournisseur a rĂ©pondu : « ClĂ© API non valide. »

SQLite aprĂšs cette soumission :

instr(ciphertext_blob, 'sk-ant') = 0

La sonde Anthropic en direct :

chiffrement/déchiffrement : correspondance
identifiants dans le contexte Navi : non
fournisseur atteint : oui
rĂ©sultat : erreur de facturation — solde crĂ©diteur trop faible

Telles sont les conclusions que je suis prĂȘt Ă  dĂ©fendre.

Ce que je n’ai pas vĂ©rifiĂ©

AWS KMS a Ă©tĂ© configurĂ© mais n’a pas Ă©tĂ© appelĂ©.

La baseUrl d’Anthropic sur Azure a Ă©tĂ© configurĂ©e mais n’a pas Ă©tĂ© appelĂ©e.

Aucune opĂ©ration payante sur Anthropic n’a abouti. Par consĂ©quent, aucun jeton n’a Ă©tĂ© rĂ©ellement reçu, et la comptabilisation des coĂ»ts n’a pas Ă©tĂ© validĂ©e par rapport Ă  une rĂ©ponse positive du fournisseur.

KeyManagement reste expérimental.

« ImplĂ©mentĂ© », « configurĂ© », « testĂ© » et « vĂ©rifiĂ© » sont quatre termes distincts. Cette semaine, j’ai utilisĂ© les quatre. Je ne les confonds pas.

Conclusion

Le fichier .env est un bon modÚle lorsque le secret appartient au déploiement.

Le BYOK change de propriétaire.

DĂšs lors qu’un identifiant appartient Ă  un utilisateur de l’application, il nĂ©cessite un cycle de vie propre Ă  l’application : saisie, chiffrement, persistance, exĂ©cution, observabilitĂ© et, Ă  terme, rotation. La gestion des clĂ©s de Symfony 8.2 est une primitive utile pour l’étape de chiffrement. Il est expĂ©rimental, et son arbre de configuration est pointilleux sur les variables d’environnement, mais les interfaces que j’ai testĂ©es — chiffrer un petit secret, stocker un Ciphertext, dĂ©chiffrer au dernier moment — sont celles dont BYOK a rĂ©ellement besoin.

Navi n’a pas besoin de devenir un gestionnaire de secrets. Symfony AI n’a pas besoin de gĂ©rer la persistance. Le formulaire n’a pas besoin de savoir comment la requĂȘte IA est construite.

Chaque couche peut rester lĂ©gĂšre. La clĂ© n’est plus une ligne dans .env. C’est une ligne de table, un texte chiffrĂ©, un argument de constructeur et un Ă©lĂ©ment de ligne. C’est lĂ  tout le changement.

Connectez-vous pour réagir à cet article

🚀 1

Site

  • Plan du Site
  • Contact
  • Mentions lĂ©gales

Network

  • Hello
  • Blog
  • Apps
  • Photos

Social

Darkwood 2026, tous droits réservés