Connection strings no appsettings.json, chaves de API em variáveis de ambiente, senhas hardcoded em código-fonte — esses problemas aparecem em praticamente todo projeto .NET que não tem uma estratégia de segredos. O Azure Key Vault resolve isso de forma centralizada, auditável e sem que nenhum desenvolvedor precise conhecer os valores reais em produção.
Por que variáveis de ambiente não bastam
Variáveis de ambiente são melhores que código-fonte, mas ainda têm problemas sérios em produção:
- Visíveis para qualquer processo rodando na mesma máquina
- Aparecem em dumps de processo e logs de sistema
- Sem auditoria de acesso (quem leu qual segredo, quando)
- Rotação exige redeploy da aplicação
- Difíceis de revogar em caso de vazamento
O Key Vault resolve todos esses pontos: segredos ficam criptografados em repouso e em trânsito, acesso é auditado, rotação é automática e a aplicação busca os valores em runtime.
Configuração inicial
dotnet add package Azure.Extensions.AspNetCore.Configuration.Secrets
dotnet add package Azure.Identity
// Program.cs — integração com IConfiguration
var builder = WebApplication.CreateBuilder(args);
// Adiciona Key Vault como fonte de configuração
if (!builder.Environment.IsDevelopment())
{
var keyVaultUri = builder.Configuration["KeyVault:Uri"]
?? throw new InvalidOperationException("KeyVault:Uri não configurado");
builder.Configuration.AddAzureKeyVault(
new Uri(keyVaultUri),
new DefaultAzureCredential()); // Usa identidade gerenciada em produção
}
// Em desenvolvimento, usa User Secrets (ver seção abaixo)
// Os segredos do Key Vault ficam disponíveis via IConfiguration
// ex: builder.Configuration["ConnectionStrings--Default"]
// (hífens duplos = separador de seção)
Autenticação: Identidade Gerenciada (o jeito certo)
A autenticação com Key Vault nunca deve usar client secret ou certificado hardcoded. Use Managed Identity — a aplicação se autentica automaticamente usando sua identidade no Azure, sem nenhuma credencial no código:
# Habilitar System-Assigned Managed Identity na App Service / AKS
az webapp identity assign \
--name minha-api \
--resource-group meu-rg
# Obter o principal ID gerado
az webapp identity show \
--name minha-api \
--resource-group meu-rg \
--query principalId
# Dar permissão de leitura de segredos ao identity
az keyvault set-policy \
--name meu-keyvault \
--object-id <principalId> \
--secret-permissions get list
# Para Azure RBAC (preferível ao Key Vault Access Policies):
az role assignment create \
--role "Key Vault Secrets User" \
--assignee <principalId> \
--scope /subscriptions/<subId>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vaultName>
// DefaultAzureCredential tenta múltiplas formas de autenticação na ordem:
// 1. EnvironmentCredential (CI/CD com variáveis AZURE_CLIENT_ID etc.)
// 2. WorkloadIdentityCredential (Kubernetes com AAD Pod Identity)
// 3. ManagedIdentityCredential (App Service, AKS, VM)
// 4. VisualStudioCredential (desenvolvedor autenticado no VS)
// 5. AzureCliCredential (desenvolvedor autenticado via az login)
builder.Configuration.AddAzureKeyVault(
new Uri(keyVaultUri),
new DefaultAzureCredential(new DefaultAzureCredentialOptions
{
// Especificar o Managed Identity client ID (se houver múltiplos)
ManagedIdentityClientId = builder.Configuration["Azure:ManagedIdentityClientId"],
// Excluir credenciais não aplicáveis ao ambiente para acelerar o fallback
ExcludeSharedTokenCacheCredential = true,
ExcludeVisualStudioCodeCredential = builder.Environment.IsProduction(),
ExcludeAzureCliCredential = builder.Environment.IsProduction()
}));
Nomenclatura de segredos: convenção para múltiplos ambientes
# Convenção no Key Vault (hífens em vez de : ou __)
# O provider converte automaticamente -- para : no IConfiguration
ConnectionStrings--Default → ConnectionStrings:Default
ConnectionStrings--Redis → ConnectionStrings:Redis
Jwt--SecretKey → Jwt:SecretKey
Jwt--Issuer → Jwt:Issuer
ExternalApis--PaymentGateway--ApiKey → ExternalApis:PaymentGateway:ApiKey
Brevo--ApiKey → Brevo:ApiKey
# Para múltiplos ambientes, use Key Vaults separados por ambiente:
# neryx-keyvault-dev
# neryx-keyvault-staging
# neryx-keyvault-prod
# Criar segredos via CLI
az keyvault secret set \
--vault-name neryx-keyvault-prod \
--name "ConnectionStrings--Default" \
--value "Server=prod-db;Database=neryx;..."
az keyvault secret set \
--vault-name neryx-keyvault-prod \
--name "Jwt--SecretKey" \
--value "$(openssl rand -base64 64)"
# Listar todos os segredos (sem os valores)
az keyvault secret list \
--vault-name neryx-keyvault-prod \
--query "[].name" -o table
Acessando segredos via IConfiguration e IOptions
// Os segredos do Key Vault ficam transparentes no IConfiguration
// Não há diferença no código entre segredos locais e do Key Vault
// Via IConfiguration diretamente
public class OrderService
{
private readonly string _connectionString;
public OrderService(IConfiguration configuration)
{
// Busca de appsettings.json em dev, Key Vault em produção
_connectionString = configuration.GetConnectionString("Default")
?? throw new InvalidOperationException("ConnectionString não configurada");
}
}
// Via IOptions (preferível para configurações estruturadas)
public class JwtSettings
{
public string SecretKey { get; set; } = string.Empty;
public string Issuer { get; set; } = string.Empty;
public string Audience { get; set; } = string.Empty;
public int ExpirationMinutes { get; set; } = 60;
}
// Program.cs
builder.Services.Configure<JwtSettings>(
builder.Configuration.GetSection("Jwt"));
// Uso no serviço
public class TokenService
{
private readonly JwtSettings _jwt;
public TokenService(IOptions<JwtSettings> jwtOptions)
=> _jwt = jwtOptions.Value;
public string GenerateToken(ClaimsIdentity identity)
{
var key = new SymmetricSecurityKey(
Encoding.UTF8.GetBytes(_jwt.SecretKey)); // Vem do Key Vault em produção
// ...
}
}
User Secrets em desenvolvimento (sem Key Vault)
# Inicializar User Secrets no projeto (armazena em ~/.microsoft/usersecrets/)
dotnet user-secrets init --project src/Api
# Adicionar segredos locais
dotnet user-secrets set "ConnectionStrings:Default" \
"Host=localhost;Database=neryx_dev;Username=dev;Password=devpassword" \
--project src/Api
dotnet user-secrets set "Jwt:SecretKey" \
"dev-secret-key-nao-use-em-producao-32chars" \
--project src/Api
# Listar segredos locais
dotnet user-secrets list --project src/Api
# No .csproj — User Secrets são carregados automaticamente em Development
# <UserSecretsId>guid-do-projeto</UserSecretsId>
// .gitignore — NUNCA commitar esses arquivos
appsettings.Development.json // Se contiver segredos reais
*.pfx
*.p12
.env
secrets.json
Cache e prefixos de segredos
// Por padrão, o provider recarrega segredos periodicamente
// Configure o intervalo de reload e filtros para otimizar
builder.Configuration.AddAzureKeyVault(
new Uri(keyVaultUri),
new DefaultAzureCredential(),
new AzureKeyVaultConfigurationOptions
{
// Recarrega segredos a cada 5 minutos (padrão: nunca)
ReloadInterval = TimeSpan.FromMinutes(5),
// Filtrar segredos por prefixo (útil quando o vault é compartilhado)
Manager = new PrefixKeyVaultSecretManager("NeryxApi")
});
// PrefixKeyVaultSecretManager filtra apenas segredos que começam com "NeryxApi--"
// e remove o prefixo ao mapear para IConfiguration
public class PrefixKeyVaultSecretManager : KeyVaultSecretManager
{
private readonly string _prefix;
public PrefixKeyVaultSecretManager(string prefix)
=> _prefix = $"{prefix}--";
public override bool Load(SecretProperties secret)
=> secret.Name.StartsWith(_prefix, StringComparison.OrdinalIgnoreCase);
public override string GetKey(KeyVaultSecret secret)
=> secret.Name[_prefix.Length..].Replace("--", ConfigurationPath.KeyDelimiter);
}
Rotação automática de segredos
# Criar nova versão de um segredo (rotação)
az keyvault secret set \
--vault-name neryx-keyvault-prod \
--name "ConnectionStrings--Default" \
--value "Server=prod-db;Password=nova-senha-rotacionada;..."
# Desativar versão antiga após confirmar que a nova está funcionando
az keyvault secret set-attributes \
--vault-name neryx-keyvault-prod \
--name "ConnectionStrings--Default" \
--version <versao-antiga> \
--enabled false
# A aplicação buscará automaticamente a versão mais recente ativa
# no próximo ciclo de reload (ReloadInterval configurado acima)
GitHub Actions: usando Key Vault no CI/CD
# .github/workflows/deploy.yml
jobs:
deploy:
runs-on: ubuntu-latest
permissions:
id-token: write # Necessário para OIDC com Azure
contents: read
steps:
- uses: actions/checkout@v4
- name: Login no Azure via OIDC (sem client secret!)
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Buscar segredos do Key Vault para o pipeline
uses: azure/get-keyvault-secrets@v1
with:
keyvault: neryx-keyvault-prod
secrets: "ConnectionStrings--Default, Jwt--SecretKey"
id: keyvault
- name: Build e testes de integração com segredos reais
env:
ConnectionStrings__Default: ${{ steps.keyvault.outputs.ConnectionStrings--Default }}
Jwt__SecretKey: ${{ steps.keyvault.outputs.Jwt--SecretKey }}
run: dotnet test --configuration Release
- name: Deploy para App Service
uses: azure/webapps-deploy@v3
with:
app-name: neryx-api-prod
# A App Service usa Managed Identity para acessar Key Vault em runtime
# Não precisa passar segredos aqui
Auditoria de acesso
# Ver quem acessou quais segredos e quando
az monitor activity-log list \
--resource-group meu-rg \
--resource-type Microsoft.KeyVault/vaults \
--resource neryx-keyvault-prod \
--query "[?operationName.value=='Microsoft.KeyVault/vaults/secrets/read']" \
--output table
# Configurar alertas para acesso suspeito (ex: muitos acessos em pouco tempo)
az monitor alert create \
--name "KeyVault-HighAccess" \
--resource neryx-keyvault-prod \
--condition "count > 1000" \
--window-size 5m \
--evaluation-frequency 1m
Checklist de segurança de segredos
- Nunca commitar segredos — use
.gitignore+git-secretsougitleaksno CI - User Secrets em desenvolvimento — não
appsettings.Development.jsoncom senhas reais - Managed Identity em produção — nunca client secret ou certificado no código
- Key Vaults separados por ambiente — dev/staging/prod com permissões diferentes
- Princípio do menor privilégio — cada aplicação acessa apenas os segredos que precisa
- Rotação automática — configure alertas 30 dias antes da expiração de certificados
- Auditoria ativa — revise logs de acesso mensalmente
- ReloadInterval configurado — para rotações sem redeploy
Vazamento de credenciais é uma das causas mais comuns de incidentes de segurança em produção. Se você quer implementar uma estratégia de segredos robusta na sua stack .NET e Azure, fale com a Neryx.