Introduction
Configuration affects how an application connects to databases, authenticates with external services, and behaves in different environments. Poor configuration practices can expose credentials, cause production failures, or make deployments difficult to reproduce.
ASP.NET Core offers a flexible configuration system, but security still depends on how values are stored, loaded, validated, and accessed.
1. Separate ordinary settings from secrets
Ordinary configuration includes values such as feature flags, timeout settings, and non-sensitive service URLs. Secrets include database passwords, API keys, private certificates, and credentials.
A typical configuration section might look like this:
{
"ExternalService": {
"BaseUrl": "https://api.example.com",
"TimeoutSeconds": 10
}
}
This can be committed to source control if it contains no sensitive information. Actual credentials should come from an appropriate secret store or protected deployment configuration.
Do not place production passwords or API keys in a committed appsettings.json file.
2. Use the options pattern
Strongly typed options make configuration easier to discover and validate.
public sealed class ExternalServiceOptions
{
public const string SectionName = "ExternalService";
public string BaseUrl { get; set; } = "";
public int TimeoutSeconds { get; set; }
}
Register the options:
builder.Services
.AddOptions<ExternalServiceOptions>()
.BindConfiguration(ExternalServiceOptions.SectionName)
.Validate(
options => Uri.TryCreate(
options.BaseUrl,
UriKind.Absolute,
out _),
"BaseUrl must be an absolute URI.")
.Validate(
options => options.TimeoutSeconds > 0,
"TimeoutSeconds must be positive.")
.ValidateOnStart();
Startup validation can reveal invalid configuration before the application begins serving traffic. Add additional checks for required schemes, permitted hosts, and other application-specific requirements.
3. Understand configuration providers
ASP.NET Core combines values from multiple providers. The default setup typically includes JSON files, environment-specific JSON files, user secrets in development, environment variables, and command-line arguments, with later providers generally overriding earlier ones.
For example, an environment variable can override a nested setting using double underscores:
ExternalService__TimeoutSeconds=20
This is useful for containers and deployment pipelines because the same application artifact can run in different environments without modifying its files.
However, environment variables are not automatically a secure secret store. Their protection depends on the hosting platform, process access, diagnostics, and deployment configuration.
4. Use User Secrets for local development
The .NET Secret Manager helps keep development secrets out of a project’s source tree.
For example:
dotnet user-secrets init
dotnet user-secrets set "ExternalService:ApiKey" "development-key"
User Secrets are intended for development convenience and are not encrypted as a production-grade secret vault. Do not use them as the storage mechanism for production credentials.
For production, prefer a managed secret store or platform-supported secret mechanism with access controls, auditing, and rotation capabilities.
5. Apply least privilege and rotation
A secret should grant only the permissions the application actually needs. A web service that reads product data should not use a database account with unrestricted administrative privileges.
A sound secrets strategy includes:
- Restricting who and what can read secrets.
- Avoiding secrets in logs, exception messages, and telemetry.
- Rotating credentials and revoking compromised values.
- Separating development, testing, staging, and production credentials.
- Reviewing access permissions periodically.
If a credential is accidentally committed to a repository, deleting it from the latest commit is not enough. Treat it as exposed and rotate or revoke it.
6. Make configuration observable without exposing secrets
It is useful to know which environment the application is running in and whether required configuration has been loaded. It is not useful to print every configuration value into a startup log.
Log non-sensitive status information and validation failures without including credential values. Configuration diagnostics should help operators identify missing or malformed settings while preserving confidentiality.
Conclusion
Secure configuration combines clear options, startup validation, environment-specific settings, protected secret storage, and least-privilege access. Keep secrets out of source control, make deployment overrides deliberate, and treat rotation and auditing as ongoing operational responsibilities.