How Do You Securely Store Secrets Used by Microservices?
In microservices, secrets should not be hard-coded in source code or committed to configuration files. In production, store them in a dedicated secret-management system and, where possible, avoid secrets entirely by using workload/managed identities.
Examples of secrets include:
Database passwords
Client secrets
API keys
Redis passwords
Message-broker credentials
Signing/private keys
Third-party credentials
Certificates/private keys
A common Azure architecture is:
Microservice
|
| Managed Identity
v
Azure Key Vault
|
+--> Database secret
+--> API secret
+--> Certificate
The key principle is:
Do not ask, "Where can I hide the secret?" First ask, "Can I eliminate the secret?"
1. Why Do We Need Secret Management?
Consider this configuration:
{
"ConnectionStrings": {
"DefaultConnection":
"Server=prod-db;Database=Orders;User Id=admin;Password=P@ss123;"
},
"Payment": {
"ClientSecret": "abc123"
}
}
If this is committed to Git:
Developer
|
v
appsettings.json
|
v
Git Repository
|
+--> Database Password
|
+--> Client Secret
Anyone who gains access to the repository, its history, build artifacts, logs, or copied configuration may obtain those credentials.
Even deleting the secret in a later commit may not remove it from Git history.
2. What Should Never Be Hard-Coded?
Avoid this:
var password = "P@ssword123";
var apiKey = "ABC-123-SECRET";
var connectionString =
"Server=prod;User Id=sa;Password=P@ssword123;";
Also avoid storing real production secrets directly in committed:
appsettings.json
appsettings.Production.json
source code
Dockerfile
docker-compose.yml
Git repository
README files
scripts
3. Recommended Strategy by Environment
A practical ASP.NET Core approach is:
| Environment | Recommended approach |
|---|---|
| Local development | .NET User Secrets |
| CI/CD | Protected pipeline secret/identity |
| Azure production | Managed Identity + Azure Key Vault |
| Kubernetes | Workload Identity + external secret manager |
| Other cloud | Cloud-native secret manager |
| On-premises | Enterprise vault/secret manager |
Examples of secret managers include:
Azure Key Vault
AWS Secrets Manager
Google Cloud Secret Manager
HashiCorp Vault
4. Best Option — Avoid the Secret
Before:
Order Service
|
| SQL Username
| SQL Password
v
Azure SQL
The application has to protect:
SQL Password
A stronger Azure architecture can use:
Order Service
|
| Managed Identity
v
Microsoft Entra ID
|
v
Azure SQL
Now there is no SQL password for the application to store.
The same principle can apply to supported Azure resources.
Avoid secret
>
Store secret securely
>
Hard-code secret
5. Azure Managed Identity
A Managed Identity gives an Azure-hosted workload an identity in Microsoft Entra ID without requiring you to manage the underlying credential yourself.
For example:
Azure App Service
Order Service
|
| Managed Identity
v
Azure Key Vault
Your code does not need:
ClientSecret = "..."
Instead it requests credentials through Azure's identity infrastructure.
6. Azure Key Vault
Azure Key Vault is commonly used to securely manage:
- secrets
- keys
- certificates
For example:
Azure Key Vault
|
+------------+------------+
| | |
v v v
DB Secret API Key Certificate
^
|
Managed Identity
|
|
Order Service
The application authenticates to Key Vault using its identity, and Key Vault authorizes access.
7. ASP.NET Core + Azure Key Vault
For a .NET application, commonly used packages include:
dotnet add package Azure.Identity
dotnet add package Azure.Extensions.AspNetCore.Configuration.Secrets
appsettings.json
Store only non-secret configuration such as the vault location:
{
"KeyVault": {
"VaultUri": "https://my-company-vault.vault.azure.net/"
}
}
No Key Vault password needs to be stored here when Managed Identity is used.
8. Program.cs
using Azure.Identity;
var builder = WebApplication.CreateBuilder(args);
var keyVaultUri =
builder.Configuration["KeyVault:VaultUri"];
if (!string.IsNullOrWhiteSpace(keyVaultUri))
{
builder.Configuration.AddAzureKeyVault(
new Uri(keyVaultUri),
new DefaultAzureCredential());
}
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
The important part is:
new DefaultAzureCredential()
9. What Is DefaultAzureCredential?
DefaultAzureCredential tries an ordered chain of supported Azure authentication mechanisms.
This makes the same application code convenient across development and Azure hosting.
Conceptually:
Local Development
|
| Developer identity /
| supported local credential
v
DefaultAzureCredential
|
v
Azure Key Vault
Production:
Azure App Service
|
| Managed Identity
v
DefaultAzureCredential
|
v
Azure Key Vault
So the source code can remain essentially the same while the credential source changes with the environment.
10. Store a Secret in Key Vault
Suppose the application needs:
ConnectionStrings:OrderDatabase
Azure Key Vault configuration uses -- to represent configuration nesting in secret names.
For example, the Key Vault secret can be named:
ConnectionStrings--OrderDatabase
with a value such as:
Server=tcp:...;Database=Orders;...
ASP.NET Core configuration can expose it as:
var connectionString =
builder.Configuration
.GetConnectionString("OrderDatabase");
Conceptually:
Key Vault
ConnectionStrings--OrderDatabase
|
v
ASP.NET Core Configuration
ConnectionStrings:OrderDatabase
11. Using It with EF Core
builder.Services.AddDbContext<OrderDbContext>(
options =>
{
options.UseSqlServer(
builder.Configuration
.GetConnectionString("OrderDatabase"));
});
The application code doesn't need to know whether the value originated from:
appsettings
Environment Variable
User Secrets
Key Vault
It reads from ASP.NET Core's configuration system.
12. Full Simplified Example
Packages
dotnet add package Azure.Identity
dotnet add package Azure.Extensions.AspNetCore.Configuration.Secrets
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
appsettings.json
{
"KeyVault": {
"VaultUri":
"https://my-company-vault.vault.azure.net/"
}
}
Program.cs
using Azure.Identity;
using Microsoft.EntityFrameworkCore;
var builder =
WebApplication.CreateBuilder(args);
// ========================================
// Azure Key Vault
// ========================================
var vaultUri =
builder.Configuration["KeyVault:VaultUri"];
if (!string.IsNullOrWhiteSpace(vaultUri))
{
builder.Configuration.AddAzureKeyVault(
new Uri(vaultUri),
new DefaultAzureCredential());
}
// ========================================
// Database
// ========================================
builder.Services.AddDbContext<OrderDbContext>(
options =>
{
options.UseSqlServer(
builder.Configuration
.GetConnectionString(
"OrderDatabase"));
});
builder.Services.AddControllers();
var app = builder.Build();
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
Architecture:
Order Service
|
| Managed Identity
v
Azure Key Vault
|
| Authorized secret access
v
Order Service
|
v
EF Core
|
v
SQL Server
13. Key Vault Authentication vs Authorization
These are separate.
Authentication
Key Vault asks:
Who are you?
Caller
=
Order Service Managed Identity
Authorization
Then:
What are you allowed to access?
For example:
Order Service
Read Order DB secret ✓
Read Payment secret ✗
Delete secrets ✗
Manage Key Vault ✗
Use least privilege.
Don't give every microservice access to every secret.
14. Each Microservice Should Have Its Own Identity
Avoid:
Order Service ------+
|
Payment Service ----+----> Same Identity
|
Inventory Service --+
Prefer:
Order Service
|
+--> OrderService Identity
Payment Service
|
+--> PaymentService Identity
Inventory Service
|
+--> InventoryService Identity
Then Key Vault permissions can be specific:
Order Service
|
+--> Order DB secret
Payment Service
|
+--> Payment provider secret
Inventory Service
|
+--> Inventory DB secret
This limits the blast radius if one service is compromised.
15. Local Development — .NET User Secrets
For local development, ASP.NET Core provides User Secrets.
Initialize:
dotnet user-secrets init
Set a value:
dotnet user-secrets set \
"ConnectionStrings:OrderDatabase" \
"Server=localhost;Database=Orders;Trusted_Connection=True;"
Read normally:
var connectionString =
builder.Configuration
.GetConnectionString("OrderDatabase");
Your code does not need special secret-reading logic.
16. Where Are User Secrets Stored?
They are stored outside the project directory and are associated with the project's UserSecretsId.
Conceptually:
Project
|
| UserSecretsId
v
User profile secret storage
Therefore they are not normally committed with the project.
Important
.NET User Secrets are intended primarily to keep development secrets out of source control.
They are not a production secret vault and should not be treated as one.
17. Environment Variables
Secrets can also be supplied through environment variables.
For example:
ConnectionStrings__OrderDatabase
maps to:
ConnectionStrings:OrderDatabase
Then:
builder.Configuration
.GetConnectionString("OrderDatabase");
can retrieve it.
Environment variables are useful in containers and deployment systems, but they still require secure injection and operational controls.
They should not automatically be considered equivalent to a dedicated secret manager.
18. Kubernetes Secrets
A basic Kubernetes Secret might look like:
apiVersion: v1
kind: Secret
metadata:
name: order-service-secret
type: Opaque
stringData:
DatabasePassword: "..."
However, an important misconception is:
Base64 is not encryption.
Kubernetes secret data may be represented using Base64, but Base64 itself provides no confidentiality.
For stronger production architectures, combine Kubernetes with appropriate controls such as:
Encryption at rest
RBAC
Workload Identity
External secret manager
Secrets Store CSI Driver
Azure Key Vault / cloud secret manager
A stronger model is:
Kubernetes Pod
|
| Workload Identity
v
Azure Key Vault
rather than permanently embedding many long-lived credentials in cluster manifests.
19. Docker Secrets
Avoid baking secrets into an image:
ENV DB_PASSWORD=P@ssword123
Anyone inspecting the image/build history may potentially expose sensitive data.
Instead:
Container Image
|
| No production secret
v
Runtime
|
| Inject identity/secret
v
Application
Secrets should normally be supplied at runtime through the deployment platform.
20. CI/CD Pipeline Secrets
Deployment pipelines also need credentials.
Avoid:
variables:
DB_PASSWORD: "P@ssword123"
in source-controlled pipeline files.
Prefer:
CI/CD Pipeline
|
| Workload/Federated Identity
v
Cloud Platform
or a protected pipeline secret store when an actual secret is unavoidable.
A modern direction is federated workload identity, which can eliminate long-lived cloud credentials from CI/CD systems.
21. Secret Rotation
Secrets should be rotatable.
Bad design:
Password created
|
v
Used for 7 years
Better:
Secret V1
|
v
Rotate
|
v
Secret V2
Rotation reduces the useful lifetime of a compromised credential.
Where possible, automate rotation and design applications so credentials can change without source-code changes.
22. Secret Versioning
Secret managers often support versions.
Conceptually:
OrderDbPassword
Version 1
↓
Version 2
↓
Version 3
This helps with:
- controlled rotation
- rollback
- auditing
- deployment transitions
23. Don't Log Secrets
A perfectly stored secret can still leak through logging.
Avoid:
_logger.LogInformation(
"Connection string: {ConnectionString}",
connectionString);
Also avoid logging:
Passwords
Client secrets
API keys
Access tokens
Refresh tokens
Private keys
Full sensitive connection strings
24. Don't Return Secrets in Exceptions
Avoid responses like:
{
"error": "Could not connect using Password=P@ss123"
}
Production errors should not expose credentials or sensitive configuration.
Use centralized exception handling and safe error responses.
25. Separate Secrets by Environment
Avoid sharing:
Development
SIT
UAT
Production
|
v
Same database password
Prefer isolation:
Development
|
+--> Development secrets
SIT
|
+--> SIT secrets
UAT
|
+--> UAT secrets
Production
|
+--> Production secrets
A compromise in development should not expose production.
26. Separate Secrets by Microservice
Likewise:
Order Service
|
+--> Order DB credential
Payment Service
|
+--> Payment credential
Notification Service
|
+--> Email/SMS credential
Avoid one giant shared credential:
GlobalMicroservicePassword
used by every service.
27. Least Privilege
Suppose Order Service needs only:
OrderDatabase
Don't grant:
OrderDatabase ✓
PaymentSecret ✗
RedisAdminPassword ✗
SigningPrivateKey ✗
NotificationSecret ✗
This is:
Principle of Least Privilege
Every service should receive only the access necessary to perform its job.
28. Secret Storage vs Encryption
These are related but different.
Encryption at rest
Protects stored data:
Secret
|
| Encrypt
v
Encrypted Storage
Encryption in transit
Protects data while being transmitted:
Microservice
|
| HTTPS / TLS
v
Secret Manager
A proper secret-management architecture normally needs both.
29. Secret Storage vs Configuration
Not everything belongs in Key Vault.
Normal configuration
PageSize = 20
RetryCount = 3
ServiceUrl
Feature settings
Logging level
These usually don't require secret storage.
Secrets
Password
Client Secret
Private API Key
Private Key
Sensitive connection credential
These do.
Don't turn your secret manager into a store for every ordinary configuration setting.
30. Production Architecture
A strong Azure microservices design could look like:
Microsoft Entra ID
|
Managed Identities
|
+--------------+--------------+
| | |
v v v
Order API Payment API Inventory API
| | |
+--------------+--------------+
|
v
Azure Key Vault
|
+----------+----------+
| | |
v v v
Secrets Keys Certificates
Each service should have access only to what it needs.
Even better, supported resources can be accessed directly through identities:
Order Service
|
| Managed Identity
v
Azure SQL
In that case:
No SQL Password
No secret retrieval
No password rotation
for the application.
31. Advantages
Secure secret management provides:
- centralized secret storage
- encryption
- access control
- auditing
- easier rotation
- environment separation
- reduced source-code exposure
- improved least privilege
- easier credential revocation
- better compliance posture
Managed/workload identities further reduce the number of credentials you need to manage.
32. Disadvantages
There are also operational costs:
- additional infrastructure
- IAM configuration
- service dependency
- permission management
- rotation complexity
- secret-manager cost in some environments
- development environment differences
You also need to design for temporary secret-manager/network failures where runtime retrieval is involved.
33. Common Mistakes
Mistake 1
Store production password in appsettings.json
Mistake 2
Commit appsettings.Production.json containing secrets
Mistake 3
Hard-code API keys
Mistake 4
Use the same secret for every microservice
Mistake 5
Give every service access to the entire Key Vault
Mistake 6
Log connection strings or tokens
Mistake 7
Never rotate secrets
Mistake 8
Treat Base64 as encryption
Mistake 9
Use client secrets when Managed Identity could remove the secret entirely
34. Key Points
For interviews, remember:
- Never hard-code production secrets.
- Never commit production secrets to Git.
- Use
.NET User Secretsfor local development. - Use a dedicated secret manager for production secrets.
- In Azure, Azure Key Vault is a common choice.
- Prefer Managed Identity where supported.
- For Kubernetes, prefer Workload Identity + external secret management where appropriate.
- Each microservice should have its own identity.
- Apply least privilege.
- Separate development/SIT/UAT/production credentials.
- Rotate secrets.
- Never log secrets.
- Protect secrets both at rest and in transit.
- Use auditing to track secret access.
- Prefer eliminating credentials over securely storing long-lived credentials.
35. Interview Questions and Answers
Q1. How do you securely store secrets in Microservices?
Answer:
I don't hard-code secrets or store production secrets in source-controlled configuration. For local development I use .NET User Secrets. In production I use a dedicated secret manager such as Azure Key Vault. On Azure, I prefer Managed Identity so the microservice can authenticate to Key Vault or supported resources without storing a client secret.
Q2. Should passwords be stored in appsettings.json?
Production passwords should not be committed in appsettings.json.
appsettings.json is appropriate for non-sensitive configuration.
Q3. Where do you store secrets during local development?
For ASP.NET Core:
.NET User Secrets
For example:
dotnet user-secrets set \
"Payment:ApiKey" \
"my-development-key"
Q4. Where do you store production secrets in Azure?
A common answer is:
Azure Key Vault
with the application accessing it through:
Managed Identity
Q5. What is Managed Identity?
Managed Identity provides an Azure workload with an identity in Microsoft Entra ID while Azure manages the underlying credentials.
This can eliminate the need to store:
Client ID + Client Secret
for many Azure-to-Azure authentication scenarios.
Q6. What is DefaultAzureCredential?
It is part of:
Azure.Identity
and provides a credential chain that allows applications to authenticate using appropriate available identities across development and Azure environments.
Q7. Should all Microservices use the same Key Vault permissions?
No.
Apply:
Least privilege
For example:
Order Service
-> Order secrets only
Payment Service
-> Payment secrets only
Q8. Is Kubernetes Secret encrypted because it uses Base64?
No.
Base64 is encoding, not encryption.
Additional Kubernetes and infrastructure security controls are required.
Q9. How often should secrets be rotated?
There is no universal interval for every credential. Rotation should follow the organization's security requirements and the credential/provider's capabilities.
More importantly, design the application so credentials can be rotated safely, and automate rotation where practical.
Q10. What is better than storing a secret securely?
Not having the secret.
For example:
Before:
Order Service
|
SQL Username + Password
|
v
Azure SQL
Better where supported:
Order Service
|
Managed Identity
|
v
Azure SQL
Short Interview Answer
I never hard-code or commit microservice secrets to source control. For local development, I use .NET User Secrets. In production, I use a dedicated secret manager such as Azure Key Vault, with each microservice having its own identity and least-privilege access. In Azure, I prefer Managed Identity so services can authenticate to Key Vault, databases, or other supported resources without storing long-lived client secrets. I also separate secrets by environment and service, rotate credentials, audit access, and make sure passwords, API keys, tokens, and connection strings are never written to logs.
The architecture to remember is:
Best
|
v
Eliminate Secret
Managed Identity
|
v
If Secret Is Required
|
v
Secret Manager
Azure Key Vault
|
v
Least-Privilege IAM
|
v
Microservice
Local Development
|
v
.NET User Secrets
Never
|
+--> Hard-coded secret
+--> Git repository
+--> Production password in appsettings
+--> Logs
The industry principle is: eliminate secrets where possible; otherwise centralize, restrict, rotate, audit, and never expose them in source code or logs.