Every ASP.NET Core application depends on secrets. Database connection strings, API keys, JWT signing keys, OAuth client secrets, SMTP credentials, and cloud service tokens are all essential for application functionality. Unfortunately, these secrets are also among the most common causes of security incidents when they are accidentally committed to source control or exposed through insecure configuration.
Hardcoding secrets in configuration files or source code may seem convenient during development, but it creates significant security risks in production. ASP.NET Core provides a flexible configuration system that enables developers to manage secrets securely across development, testing, and production environments.
In this article, you'll learn how to manage secrets securely using User Secrets during development, Azure Key Vault in production, and configuration best practices that help protect sensitive information throughout the application lifecycle.
Why Secrets Management Matters
The Risks of Hardcoding Secrets
Consider the following configuration:
{
"ConnectionStrings": {
"DefaultConnection": "Server=sql01;Database=SalesDb;User Id=admin;Password=P@ssw0rd!"
}
}
Although this works, storing credentials directly in configuration files introduces several risks:
Accidental commits to Git repositories
Exposure through backups
Shared development credentials
Difficult secret rotation
Increased security audit findings
Sensitive information should never be stored directly in application source code or tracked configuration files.
Understanding ASP.NET Core Configuration
ASP.NET Core loads configuration from multiple providers.
Typical configuration sources include:
Configuration providers are layered, allowing secure values to override default settings without modifying application code.
Using User Secrets During Development
User Secrets provide a secure location for development-only configuration.
Initialize User Secrets:
dotnet user-secrets init
Store a connection string:
dotnet user-secrets set "ConnectionStrings:DefaultConnection" "Server=localhost;Database=SalesDb;Trusted_Connection=True;"
Read the value normally:
var connectionString =
builder.Configuration
.GetConnectionString("DefaultConnection");
Why Use User Secrets?
User Secrets store sensitive values outside the project directory.
This prevents accidental commits while allowing developers to access configuration through the standard ASP.NET Core configuration system without changing application code.
Using Environment Variables
Production environments often inject secrets through environment variables.
Example:
ConnectionStrings__DefaultConnection
ASP.NET Core automatically maps double underscores (__) to nested configuration keys.
Why Environment Variables?
Environment variables separate configuration from application binaries.
They work well with:
Docker
Kubernetes
Azure App Service
GitHub Actions
Azure DevOps
CI/CD pipelines
This approach simplifies deployment while avoiding sensitive configuration files.
Integrating Azure Key Vault
Azure Key Vault centralizes secret management for production applications.
Register Key Vault during startup.
builder.Configuration.AddAzureKeyVault(
new Uri(builder.Configuration["KeyVault:VaultUri"]!),
new DefaultAzureCredential());
Why Azure Key Vault?
Azure Key Vault provides:
Instead of distributing secrets across multiple servers, applications retrieve them securely at runtime.
Accessing Secrets
Once configuration providers are registered, consuming secrets remains identical regardless of their source.
var apiKey =
builder.Configuration["PaymentGateway:ApiKey"];
Why Is This Important?
Business code does not need to know where a secret originates.
Whether the value comes from User Secrets, an environment variable, or Azure Key Vault, the application accesses it through the same configuration API.
This keeps application code simple while allowing deployment environments to determine how secrets are supplied.
End-to-End Implementation
Consider a cloud-hosted order management system.
Architecture:
Developer
│
User Secrets
│
▼
ASP.NET Core Application
│
Configuration System
│
▼
Azure Key Vault
│
▼
SQL Database
Payment Gateway
Storage Account
Workflow:
Developers use User Secrets during local development.
The application reads configuration through the standard configuration system.
In production, secrets are retrieved from Azure Key Vault.
Managed identities authenticate the application without storing credentials.
Business services consume secrets without knowing their underlying source.
This approach provides a consistent programming model while significantly improving security across environments.
Secret Rotation
Production secrets should never remain unchanged indefinitely.
A typical rotation process includes:
Create a new secret.
Update the secure secret store.
Restart or refresh application configuration if required.
Validate application functionality.
Remove the old secret.
Planning for secret rotation reduces operational risk and supports security compliance requirements.
Configuration Source Comparison
| Configuration Source | Development | Production | Recommended |
|---|
| appsettings.json | Yes (non-sensitive settings) | Limited | Yes |
| User Secrets | Yes | No | Yes |
| Environment Variables | Yes | Yes | Yes |
| Azure Key Vault | Optional | Yes | Yes |
| Hardcoded Values | No | No | Never |
Each configuration provider serves a specific purpose, but sensitive production credentials should always be stored in a secure secret management solution.
Best Practices
Never commit secrets to source control.
Use User Secrets for local development.
Store production secrets in Azure Key Vault or an equivalent secret manager.
Use managed identities whenever possible.
Rotate secrets regularly.
Restrict access using the principle of least privilege.
Separate application configuration from secret values.
Audit secret access.
Remove unused credentials promptly.
Common Mistakes
One common mistake is storing production credentials in appsettings.json. Even private repositories can become compromised, making tracked configuration files an unsuitable location for secrets.
Another issue is sharing the same credentials across development, testing, and production environments. Each environment should use separate credentials to reduce the impact of accidental exposure.
Developers also sometimes hardcode API keys directly into application code during testing and forget to remove them before deployment.
Testing and Validation
Before deploying an application, validate the following:
User Secrets load correctly during development.
Environment variables override default configuration.
Azure Key Vault connectivity succeeds.
Managed identity authentication works.
Missing secrets generate meaningful errors.
Secret rotation procedures are documented.
Access permissions follow least-privilege principles.
Configuration works consistently across environments.
Automated deployment validation helps identify configuration issues before applications reach production.
Performance Considerations
Secret retrieval generally occurs during application startup or configuration loading, resulting in minimal runtime overhead.
To maintain good performance:
Avoid repeatedly requesting secrets.
Cache configuration values where appropriate.
Use managed identities to simplify authentication.
Monitor Key Vault latency.
Minimize unnecessary configuration lookups.
Handle temporary connectivity failures gracefully.
Security should never be sacrificed for marginal performance gains.
Security Considerations
Secrets management is only one part of an application's overall security posture.
Follow these recommendations:
Encrypt communication using HTTPS.
Enable audit logging for secret access.
Protect managed identities.
Restrict Key Vault permissions.
Remove unused secrets.
Monitor unusual access patterns.
Enable backup and recovery for secret stores.
Include secret management in regular security reviews.
Strong operational practices are just as important as secure storage.
Troubleshooting
Secret Value Is Missing
Verify that the correct configuration provider is registered and that the expected configuration key exists in the current environment.
Azure Key Vault Access Fails
Confirm that the application's managed identity or service principal has permission to read secrets from the vault.
Incorrect Configuration Is Loaded
Review the configuration provider order. Later providers override earlier ones, so provider registration order directly affects which values are returned.
Development Configuration Works but Production Fails
Ensure production secrets exist in the secure secret store and that environment-specific configuration is correctly deployed.
Conclusion
Managing secrets securely is a fundamental requirement for production ASP.NET Core applications. User Secrets provide a safe development experience, environment variables simplify cloud deployments, and Azure Key Vault offers centralized, enterprise-grade secret management for production environments. By separating secrets from application code, planning for secret rotation, and following secure configuration practices, developers can build applications that are easier to manage, more secure, and better prepared for modern deployment environments.