Dans cet article, nous allons réaliser un projet ASP.NET Core pour illustrer comment stocker les secrets avec le Secret Manager et les variables d’environnement.

Qu’est-ce qu’un secret ?

Dans une application, nous avons fréquemment besoin de nous connecter à des ressources ou des services tiers, protégés par des mots de passe, des jetons, ou d’autres systèmes d’identification. Les informations sensibles que nous ne voulons pas divulguer sont appelées secrets. L’exposition de secrets dans le code source peut compromettre la sécurité et l’intégrité de vos applications. Encore trop souvent, nous retrouvons des données sensibles dans les fichiers de configuration.

Stockage des secrets

En ASP.NET Core, il est possible de sauvegarder les secrets dans plusieurs emplacements sécurisés. Ils diffèrent en fonction du contexte de l’application. Les secrets utilisés pour les environnements de production ne doivent pas être accessibles de la même manière et par les mêmes personnes que les secrets en environnements de développement ou de tests. Les secrets de développement sont généralement stockés dans des variables d’environnement ou avec l’outil Secret Manager d’ASP.NET Core. Pour les secrets d’environnement un peu plus sensibles comme ceux de la production, il est préférable d’utiliser des coffres qui offrent un stockage plus sécurisé : Azure Key Vault en environnement Azure, ou bien Kubernetes Secrets ou Docker Vault dans des environnements conteneurisés multi Cloud.

Mise en place de l’application de démonstration

Pour la démonstration, j’ai créé une application ASP.NET Core MVC permettant d’afficher les valeurs de configuration pendant le runtime de l’application.

Présentation du fichier appsettings.json

En ASP.NET Core, le fichier de configuration standard se nomme appsettings.json et se trouve à la racine du projet. C’est dans ce fichier que l’on place les données nécessaires pour notre application (URL, chemin de fichier, configuration de logging, etc.).

appsettings.json dans l'arborescence

Voici le contenu par défaut du fichier :  

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

Dans le cadre de notre application, nous souhaitons récupérer des données à partir d’une API interne nommée CustomApi. Les éléments nécessaires pour récupérer les données de l’API sont : une URL comme point d’entrée et un token permettant de signer nos requêtes. Pour des questions de sécurité, nous ne voulons pas divulguer l’adresse de l’interface de programmation CustomApi à tous les acteurs du projet (les développeurs n’ont pas besoin d’accéder à l’adresse de production). De plus, le token doit être unique et lié à une seule personne. CustomApi:BaseUrl et CustomApi:Token sont donc des secrets. Malheureusement, fréquemment le premier réflexe est de sauvegarder l’adresse de l’API ainsi que le token dans le fichier appsettings au risque de partager les informations sensibles avec les autres développeurs.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "CustomApi": {
    "BaseUrl": "https://api-address-in-app-settings.com",
    "Token": "token in appsettings"
  }
}

Mettre les secrets dans le fichier appsettings.Development.json n’est pas plus sécurisé et peut également vite se retrouver sur le dépôt.

Chargement de la configuration

Lors de l’exécution de notre application, la méthode CreateDefaultBuilder charge la configuration.

Program.cs CreateDefaultBuilder

La configuration est chargée dans la priorité suivante :

  1. Arguments de ligne de commande.
  2. Variables d’environnement.
  3. L’outil Secret Manager quand l’application s’exécute dans l’environnement Development.
  4. appsettings.{Environment}.json.
  5. appsettings.json.

Dans ce blog, nous allons voir plus en détail les trois cas suivants :

  • appsettings.json
  • L’outil Secret Manager
  • Variables d’environnement.

CreateDefaultBuilder configure également le serveur Kestrel et la racine du contenu. Pour en savoir plus, je vous invite à regarder la page Microsoft sur l’Hôte Web ASP.NET Core.

Récupération des données de configuration

Nous pouvons récupérer la configuration dans le code avec la fonctionnalité de liaison de configuration de l’interface IConfiguration (nécessite Microsoft.Extensions.Configuration). Pour l’exemple, nous allons afficher les valeurs de la CustomApi dans la vue Index du contrôleur Home.

Dans HomeController, nous allons injecter IConfiguration dans le constructeur et sauvegarder la valeur pour pouvoir la réutiliser dans les méthodes du contrôleur.

DI IConfiguration HomeController.cs

Avec l’injection, nous récupérons les valeurs pour les passer à notre vue.

Récupération section pour le passer à la vue Index

_configuration.GetSection("CustomApi").Get<CustomApiSettingsViewModel>(); permet de récupérer la section CustomApi dans notre configuration (ici appsettings.json) et de mapper les valeurs avec les propriétés de la classe CustomApiSettingsViewModel.

return View(customApiSettings) permet d’envoyer les settings à la vue Index.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "CustomApi": {
    "BaseUrl": "https://api-address-in-app-settings.com",
    "Token": "token in appsettings"
  }
}

Les secrets CustomApi:BaseUrl et CustomApi:Token sont mappés avec les propriétés de la classe CustomApiSettingsViewModel.

CustomApiSettingsViewModel avec BaseUrl et Token

Dans la vue Index du contrôleur Home, nous pouvons afficher les valeurs de la manière suivante :

Vue Index

Après avoir exécuté l’application, voici le résultat :

Résultat secrets dans appsettings

Les valeurs du fournisseur de configuration ont été chargées pendant le runtime.

Attention ! Il ne faut jamais afficher les secrets pour une application en développement ou en production au risque de perdre l’intérêt du stockage sécurisé. Ceci est uniquement réalisé dans un cadre pédagogique.

Stockage des secrets dans le gestionnaire de secret

Qu’est-ce que le gestionnaire de secret ?

Le gestionnaire de secret permet de stocker les données sensibles pendant le développement d’une application ASP.NET Core. Les secrets ne sont pas stockés dans le projet, mais dans un dossier en local afin qu’ils ne soient pas archivés dans le logiciel de gestion de code.

Implémentation des secrets en utilisant le gestionnaire de secret

Sur le Projet, cliquez avec le bouton droit puis sélectionnez Manage User Secrets.

Manage User Secrets

Un fichier secrets.json s’ouvre, vous pouvez y configurer vos secrets.

Fichier secrets.json

Lorsque vous ouvrez le fichier .csproj, vous remarquerez que Visual Studio a ajouté un UserSecretsId afin de trouver votre fichier secret.json sur votre ordinateur. Pour visualiser le fichier, vous pouvez faire un clic droit sur le projet puis Edit Project File.

Edit Project File

Pour des questions de sécurité, j’ai volontairement caché la valeur du UserSecretsId en la remplaçant par la valeur user_secrets_id.

Fichier de projet avec la valeur de UserSecretsId

Le chemin d’accès au fichier secrets.json créé par le gestionnaire de secret sur Windows est le suivant :

%APPDATA%\Microsoft\UserSecrets\user_secrets_id\secrets.json

Lorsque nous lançons l’application, nous avons les valeurs suivantes :

Résultat gestionnaire de secret

Comme l’outil Secret Manager a une priorité plus importante que le fichier appsettings.json, les valeurs chargées par la méthode CreateDefaultBuilder sont celles du fichier secrets.json.

Program.cs CreateDefaultBuilder

Bravo ! Nous venons de lire les secrets à partir du fichier secrets.json. N’oubliez pas de supprimer les secrets du fichier appsettings.json.

Stockage des secrets dans des variables d’environnement

Qu’est ce qu’une variable d’environnement ?

Une variable d’environnement est une variable dynamique qui peut être utilisée par plusieurs processus d’un OS. Il est donc possible à partir d’un programme de faire référence à cette variable.

Implémentation des secrets à partir des variables d’environnement

Les variables d’environnement sont récupérées par CreateDefaultBuilder.

Program.cs CreateDefaultBuilder

Sous Windows, il est possible d’ajouter des variables d’environnement utilisateur avec la commande setx. L’option /M permet de la créer dans l’environnement système.

Ouvrez une console administrateur et réalisez les commandes suivantes :

setx CustomApi__BaseUrl "https://api-address-in-environment-variable.com"   /M

setx CustomApi__Token "Token in environment variable" /M

Ajout des variables d'environnement avec la console

Le séparateur « » (par exemple section:nomChampDansSection) n’est pas interprété correctement sur tous les OS pour les variables d’environnement. Le double « __ » permet de remplacer le « » (CustomApi:BaseUrl devient CustomApi__BaseUrl).

Redémarrez l’ordinateur pour que le système puisse prendre en compte les nouvelles variables d’environnement.

Comme vu précédemment, CreateDefaultBuilder charge les valeurs dans la priorité suivante :

  1. Arguments de ligne de commande.
  2. Variables d’environnement.
  3. L’outil Secret Manager quand l’application s’exécute dans l’environnement Development.
  4. appsettings.{Environment}.json.
  5. appsettings.json.

Lorsque nous lançons l’application, nous chargeons la valeur des variables environnement, car elles ont une priorité plus importante que l’outil Secret Manager et le fichier appsettings.json.

Résultat avec les variables d'environnement

Les variables d’environnement sont bien prises en compte.

Attention : Une variable d’environnement est généralement stockée en clair et non chiffrée sur l’ordinateur en question. Si quelqu’un accède à l’ordinateur, il peut récupérer facilement les secrets du développeur.

N’oubliez pas de supprimer les variables d’environnement, car vous n’en aurez plus besoin. Vous pouvez les supprimer en tapant Modifier les variables d’environnement système dans la barre de recherche Windows. Une fenêtre Propriétés système s’ouvre. Cliquez sur variables d’environnement…

Panel modifier les variables d'environnement système

Dans Variables système, vous pouvez retrouver les variables CustomApi__BaseUrl et CustomApi__Token. Sélectionnez la première variable et appuyez sur Supprimer pour supprimer la variable. Recommencez la manipulation avec la deuxième variable, puis appuyer sur OK pour sauvegarder l’état.

Les variables d’environnement système seront supprimées définitivement après le redémarrage de l’ordinateur.

Suppression des variables d'environnement

 

Configuration des variables d’environnement en production/recette

Selon le type de contexte d’exécution en test/production, les variables d’environnement seront bien sûr configurées différemment :

  • VMs Windows ou Linuw : variables d’environnement via commandes en ligne comme en développement
  • Azure App Services : les variables d’environnement sont positionnables au niveau de la configuration de l’App Service pour l’ensemble de l’application ou pour un slot spécifique (staging/production) via le portail Azure de l’App Service : onglet « Configuration », ou via des commande Azures CLI comme az webapp config appsettings set
  • Conteneurs Docker Windows ou Linux : le positionnement des variables d’environnement peut se faire de diverses façons selon le mode de déploiement, par exemple docker run –env pour un Docker engine local simple, az container create –environment-variables pour une instance Azure Container, ou bien un élément env: dans un fichier de configuration yaml pour Kubernetes.

Conclusion

Lors du développement d’une application ASP.NET Core, nous avons vu qu’il ne faut surtout pas référencer des données sensibles dans le projet. La gestion des secrets avec les variables d’environnement et le gestionnaire de secrets permet de sortir les données sensibles et de les mettre à l’extérieur du code source afin d’éviter de pousser les secrets sur un dépôt. Cependant, les secrets stockés dans les variables et le gestionnaire ne sont pas encryptées sur l’ordinateur du développeur et peuvent être uniquement utilisés pendant la phase de développement. Pour aller plus loin, je vous invite à regarder la documentation des Azure Key Vault pour stocker de manière sécurisée des secrets d’une application en production. Pour en savoir plus, voici une liste des fournisseurs de configuration disponible en ASP.NET Core.

Sources

https://docs.microsoft.com/fr-fr/aspnet/core/security/key-vault-configuration?view=aspnetcore-3.1

https://docs.microsoft.com/fr-fr/aspnet/core/security/app-secrets?view=aspnetcore-3.1&tabs=windows

https://docs.microsoft.com/fr-fr/dotnet/architecture/microservices/secure-net-microservices-web-applications/developer-app-secrets-storage

https://docs.microsoft.com/fr-fr/aspnet/core/fundamentals/host/web-host?view=aspnetcore-3.1

https://www.datree.io/resources/secrets-management-aws

https://owasp.org/www-community/vulnerabilities/Password_Plaintext_Storage

https://aspnetcore.readthedocs.io/en/stable/security/app-secrets.html

https://blog.elmah.io/appsettings-in-aspnetcore/