All code is illustrative and unexecuted.
.NET Framework protected configuration can transparently protect selected configuration sections. The historical utility name is aspnet_regiis.exe, not aspnet_regiss.exe; provider selection matters, and protected configuration is not synonymous with DPAPI alone. ASP.NET Core's JSON configuration provider does not automatically apply that legacy mechanism to appsettings.json.
Prefer keeping production secrets out of source-controlled configuration and using an appropriate existing secret-management system. Data Protection can protect local configuration values, but it does not hide them from the application identity that must decrypt them. Development User Secrets are not encrypted storage. The following narrower example is for a single Windows machine and one service identity.
Register an explicit read-only projection
This illustrative extension replaces the original mutable IConfiguration wrapper. It reads two required ciphertext values and creates an IOptions<ProtectedValues> instance. Each property has a distinct purpose chain. Missing or invalid ciphertext fails instead of becoming an empty secret. Because the options are a singleton snapshot, this example does not implement reload-on-change or writing JSON files.
using System;
using System.IO;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
public sealed class ProtectedValues
{
public string Secret1 { get; init; } = "";
public string Secret2 { get; init; } = "";
}
public static class ProtectedConfiguration
{
public const string ApplicationName = "SysSeder.ProtectedConfig.v1";
public static IServiceCollection AddProtectedValues(
this IServiceCollection services, IConfiguration configuration,
string absoluteKeyDirectory)
{
if (!OperatingSystem.IsWindows())
throw new PlatformNotSupportedException("This example requires Windows DPAPI.");
if (!Path.IsPathFullyQualified(absoluteKeyDirectory)
|| !Directory.Exists(absoluteKeyDirectory))
throw new ArgumentException("Provide a pre-provisioned absolute key directory.");
services.AddDataProtection()
.SetApplicationName(ApplicationName)
.PersistKeysToFileSystem(new DirectoryInfo(absoluteKeyDirectory))
.ProtectKeysWithDpapi(protectToLocalMachine: false);
services.AddSingleton<IOptions<ProtectedValues>>(provider =>
{
var dp = provider.GetRequiredService<IDataProtectionProvider>();
string Read(string property)
{
string ciphertext = configuration[$"ProtectedValues:{property}"]
?? throw new InvalidOperationException($"Missing protected value: {property}");
return ForProperty(dp, property).Unprotect(ciphertext);
}
return Options.Create(new ProtectedValues
{
Secret1 = Read(nameof(ProtectedValues.Secret1)),
Secret2 = Read(nameof(ProtectedValues.Secret2))
});
});
return services;
}
public static IDataProtector ForProperty(IDataProtectionProvider dp, string property)
{
if (property != nameof(ProtectedValues.Secret1)
&& property != nameof(ProtectedValues.Secret2))
throw new ArgumentException("Unknown protected property.");
return dp.CreateProtector("SysSeder.Configuration.v1", "ProtectedValues", property);
}
// Call only in an authorized local administrative utility, not an HTTP action.
public static string ProtectForStorage(IDataProtectionProvider dp,
string property, string plaintext)
{
ArgumentNullException.ThrowIfNull(plaintext);
return ForProperty(dp, property).Protect(plaintext);
}
}The absolute key-directory argument is an application deployment setting, not a literal path to copy. Provision it outside the web root with read/write permissions restricted to the service and authorized administrators. Explicit persistence disables automatic at-rest protection, so the sample explicitly uses current-user DPAPI. Machine-wide DPAPI is not selected.
Register and consume the values
Add the extension above to the project and use it in the existing application startup. The shared ASP.NET Core framework supplies Data Protection, configuration, DI and options APIs; a separate local utility needs compatible Data Protection dependencies too. No package version or runtime execution has been verified here.
// In the existing ASP.NET Core application's service-registration code:
string keyDirectory = builder.Configuration["DataProtection:KeyDirectory"]
?? throw new InvalidOperationException("Missing key-directory setting.");
builder.Services.AddProtectedValues(builder.Configuration, keyDirectory);
// After building the host, resolve once to detect missing/invalid values at startup:
var app = builder.Build();
_ = app.Services.GetRequiredService<IOptions<ProtectedValues>>().Value;
// Continue configuring the application's existing request pipeline.Consumers inject IOptions<ProtectedValues> and read Value.Secret1 or Value.Secret2. Inject IDataProtectionProvider only into components that actually need to protect or unprotect data. Changing an IConfiguration value is not a general-purpose way to persist a protected appsettings file.
Provision ciphertext without an HTTP encryption endpoint
Use ProtectForStorage from an authorized local utility configured with the same key ring, application name and property purpose. For this current-user DPAPI example it must run under the same Windows identity and compatible machine context. Enter plaintext through a protected input channel; do not put secrets in URL query strings, command-line arguments or logs. Store only the resulting ciphertext in the intended configuration property. The example deliberately removes the original GET controller.
Back up the protected key ring together with the information required to recover its protection. Retain keys needed to decrypt existing values; deleting the ring can make those values unrecoverable. Multi-machine deployment requires a deliberately designed shared-key and at-rest-protection scheme, not copying this Windows-local setup. Purpose and application names are not a security boundary against a compromised process with key access, and encryption at rest does not prevent malicious writes to the key repository. No directory, key, account or configuration was changed for this article.
Primary sources
- Microsoft protected configuration
- Configure ASP.NET Core Data Protection
- Data Protection purpose strings
- ASP.NET Core development secrets
- Data Protection key management and lifetime
- ProtectKeysWithDpapi API
- CreateProtector API
Original English article: Implementing protection of a section in appsettings.json in .NET core. Original publication date: 2021-10-14T16:25:00.018+03:00.