用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/diegosouzapw/awesome-omni-skill --skill dotnet-secrets-management命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
基于 SOC 职业分类
正在显示 SKILL.md
| name | dotnet-secrets-management |
| description | Manages secrets and sensitive config. User secrets, environment variables, rotation. |
| license | MIT |
| targets | ["*"] |
| tags | ["security","dotnet","skill"] |
| version | 0.0.1 |
| author | dotnet-agent-harness |
| invocable | true |
| claudecode | {"allowed-tools":["Read","Grep","Glob","Bash","Write","Edit"]} |
| codexcli | {"short-description":".NET skill guidance for security tasks"} |
| opencode | {"allowed-tools":["Read","Grep","Glob","Bash","Write","Edit"]} |
Cloud-agnostic secrets management for .NET applications. Covers the full lifecycle: user secrets for local development, environment variables for production, IConfiguration binding patterns, secret rotation, and managed identity as a production best practice. Includes anti-patterns to avoid (secrets in source, appsettings.json, hardcoded connection strings).
Cross-references: [skill:dotnet-security-owasp] for OWASP A02 (Cryptographic Failures) and deprecated pattern warnings, [skill:dotnet-csharp-configuration] for Options pattern and configuration source precedence.
| Environment | Secret Source | Mechanism |
|---|---|---|
| Local dev | User secrets | dotnet user-secrets CLI, secrets.json outside repo |
| CI/CD | Pipeline variables | Injected as environment variables, never in YAML |
| Staging/Production | Environment variables or vault | OS-level env vars, managed identity, or vault provider |
Principle: Secrets must never exist in the source repository or in any file committed to version control. Each environment tier uses the appropriate mechanism for its trust boundary.
User secrets store sensitive configuration outside the project directory in the user profile, preventing accidental commits.
# Initialize user secrets for a project (creates UserSecretsId in csproj)
dotnet user-secrets init
# Set individual secrets
dotnet user-secrets
dotnet user-secrets
dotnet user-secrets
dotnet user-secrets list
dotnet user-secrets remove
dotnet user-secrets clear
```text
User secrets are stored at:
- **Windows:** `%APPDATA%\Microsoft\UserSecrets\<UserSecretsId>\secrets.json`
- **macOS/Linux:** `~/.microsoft/usersecrets/<UserSecretsId>/secrets.json`
The `secrets.json` file is plain JSON with the same structure as `appsettings.json`:
```json
{
: {
:
},
: {
:
},
: {
:
}
}
```text
User secrets are loaded automatically by `WebApplication.CreateBuilder` and `Host.CreateDefaultBuilder` when `DOTNET_ENVIRONMENT` or `ASPNETCORE_ENVIRONMENT` is `Development`:
```csharp
var builder = WebApplication.CreateBuilder(args);
// User secrets are already loaded. Access them via IConfiguration:
var connectionString = builder.Configuration.GetConnectionString();
```text
For non-web hosts (console apps, worker services):
```csharp
var builder = Host.CreateApplicationBuilder(args);
// User secrets are loaded automatically Development environment.
// For explicit control:
(builder.Environment.IsDevelopment())
{
builder.Configuration.AddUserSecrets<Program>();
}
```text
**Gotcha:** User secrets are not encrypted -- they are just stored outside the repo. They are appropriate development only, never production.
---
Environment variables are the standard mechanism injecting secrets into production applications without touching the filesystem.
In the default ASP.NET Core configuration stack, environment variables override file-based sources (last wins):
1. `appsettings.json`
2. `appsettings.{Environment}.json`
3. User secrets (Development only)
4. **Environment variables** (overrides all above)
5. Command-line arguments
.NET maps environment variables to configuration keys using `__` (double underscore) as the section separator:
```bash
ConnectionStrings__DefaultDb=
Smtp__ApiKey=
Jwt__SigningKey=
MYAPP_ConnectionStrings__DefaultDb=
```text
```csharp
// Load prefixed environment variables
builder.Configuration.AddEnvironmentVariables(prefix: );
// Access the same way as any configuration :
var smtpKey = builder.Configuration[];
```text
```yaml
services:
api:
image: myapp:latest
environment:
- ConnectionStrings__DefaultDb=Server=db;Database=myapp;User=sa;Password=
- Smtp__ApiKey=
env_file:
- .
```text
```dockerfile
ENV ASPNETCORE_URLS=http://+:8080
```dockerfile
**Gotcha:** Environment variables are visible to all processes under the same user. In multi-tenant container environments, use container-level isolation (Kubernetes secrets, Docker secrets) rather than host-level vars.
---
Bind secrets to strongly typed options classes compile-time safety and validation.
```csharp
public sealed class JwtOptions
{
public const string SectionName = ;
[Required, MinLength(32)]
public string SigningKey { get; ; } = ;
/// <summary>
/// Previous signing key retained during rotation window.
/// Set this when rotating keys so tokens signed with the old key
/// remain valid they expire. Remove after rotation completes.
/// </summary>
public string? PreviousSigningKey { get; ; }
[Required]
public string Issuer { get; ; } = ;
[Required]
public string Audience { get; ; } = ;
[Range(1, 1440)]
public int ExpirationMinutes { get; ; } = 60;
}
// Registration with validation
builder.Services
.AddOptions<JwtOptions>()
.BindConfiguration(JwtOptions.SectionName)
.ValidateDataAnnotations()
.ValidateOnStart(); // Fail fast secrets are missing
```text
```csharp
// Inject and use
public sealed class TokenService(IOptions<JwtOptions> jwtOptions)
{
private JwtOptions _jwt = jwtOptions.Value;
public string GenerateToken(string userId)
{
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_jwt.SigningKey));
var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _jwt.Issuer,
audience: _jwt.Audience,
claims: [new Claim(ClaimTypes.NameIdentifier, userId)],
expires: DateTime.UtcNow.AddMinutes(_jwt.ExpirationMinutes),
signingCredentials: credentials);
new JwtSecurityTokenHandler().WriteToken(token);
}
}
```text
> Options classes must use `{ get; ; }` (not `{ get; init; }`) because the configuration binder and `PostConfigure` need to mutate properties after construction. Use data annotation attributes (`[Required]`, `[MinLength]`) validation.
**Gotcha:** `ValidateOnStart()` catches missing secrets at application startup rather than at first use. Always use it secrets-bearing options to fail fast with a clear error message.
---
Design applications to handle secret rotation without downtime.
```csharp
// Use IOptionsMonitor<T> secrets that may change at runtime
public sealed class EmailService(IOptionsMonitor<SmtpOptions> smtpOptions, ILogger<EmailService> logger)
{
public async Task SendAsync(string to, string subject, string body)
{
// CurrentValue reads the latest configuration on every call
var options = smtpOptions.CurrentValue;
logger.LogDebug(, options.Host);
// ... send email using current options ...
}
}
// Audit- configuration changes via a hosted service.
// IHostedService is always activated by the host, so the subscription is guaranteed.
public sealed class SmtpOptionsChangeLogger(
IOptionsMonitor<SmtpOptions> monitor,
ILogger<SmtpOptionsChangeLogger> logger) : IHostedService, IDisposable
{
private IDisposable? _subscription;
public Task StartAsync(CancellationToken cancellationToken)
{
_subscription = monitor.OnChange(options =>
{
logger.LogInformation(, DateTime.UtcNow);
});
Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
public void Dispose() => _subscription?.Dispose();
}
// Registration:
builder.Services.AddHostedService<SmtpOptionsChangeLogger>();
```text
```csharp
// Dual-key validation zero-downtime rotation
// Accept both old and new signing keys during rotation window
public sealed class DualKeyTokenValidator(IOptionsMonitor<JwtOptions> optionsMonitor)
{
public TokenValidationParameters ()
{
// Read CurrentValue on every call so rotated keys are picked up
// without restarting the application
var options = optionsMonitor.CurrentValue;
var keys = new List<SecurityKey>
{
new SymmetricSecurityKey(Encoding.UTF8.GetBytes(options.SigningKey))
};
(!string.IsNullOrEmpty(options.PreviousSigningKey))
{
keys.Add(new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(options.PreviousSigningKey)));
}
new TokenValidationParameters
{
ValidateIssuer = ,
ValidIssuer = options.Issuer,
ValidateAudience = ,
ValidAudience = options.Audience,
ValidateLifetime = ,
IssuerSigningKeys = keys
};
}
}
```text
1. Deploy the new secret alongside the old one (dual-key window)
2. Update the application to accept both old and new secrets
3. Roll the deployment so all instances use the new secret signing/encrypting
4. After all clients have rotated, remove the old secret
5. Audit- every rotation event
---
Managed identity eliminates secrets entirely cloud-hosted applications by using the platform
Primary approach: Use Serena symbol operations for efficient code navigation:
serena_find_symbol instead of text searchserena_get_symbols_overview for file organizationserena_find_referencing_symbols for impact analysisserena_replace_symbol_body for clean modificationsWhen to use Serena vs traditional tools:
Example workflow:
# Instead of:
Read: src/Services/OrderService.cs
Grep: "public void ProcessOrder"
# Use:
serena_find_symbol: "OrderService/ProcessOrder"
serena_get_symbols_overview: "src/Services/OrderService.cs"