var connectionString = "Server=prod-db;Password=secret123";
var apiKey = "sk-proj-xxxxx";
Always do this
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
var apiKey = builder.Configuration["ExternalApi:Key"];
// Verify at startupif (string.IsNullOrEmpty(apiKey))
thrownew InvalidOperationException("ExternalApi:Key not configured");
Azure Key Vault (Production)
// Azure Key Vault integration
builder.Configuration.AddAzureKeyVault(
new Uri($"https://{vaultName}.vault.azure.net/"),
new DefaultAzureCredential());
Checklist:
No hardcoded connection strings, API keys, or passwords
Secrets in User Secrets (dev), Azure Key Vault / env vars (prod)
Azure Key Vault configured for production deployments
appsettings.Development.json in .gitignore
No secrets in git history (git log -p -S "password")
### File Upload Validation
```csharp
[HttpPost("upload")]
public async Task<IActionResult> Upload(IFormFile file)
{
if (file.Length > 5 * 1024 * 1024)
return BadRequest("File too large (max 5MB)");
var allowedTypes = new[] { "image/jpeg", "image/png", "application/pdf" };
if (!allowedTypes.Contains(file.ContentType))
return BadRequest("Invalid file type");
var ext = Path.GetExtension(file.FileName).ToLowerInvariant();
if (!new[] { ".jpg", ".jpeg", ".png", ".pdf" }.Contains(ext))
return BadRequest("Invalid extension");
// Use a generated filename, never the user-provided one
var safeName = $"{Guid.NewGuid()}{ext}";
// ...
}
Checklist:
All API inputs validated with Result-based validation (Results) or DataAnnotations
File uploads restricted (size, type, extension)
Never trust client-side validation alone — always validate server-side
Error messages don't leak internal details
3. SQL Injection Prevention
Note: If using Event Sourcing (e.g., EventSourcing with Azure Blob Storage),
SQL injection is not applicable. Focus on blob key validation and event stream access control instead.
Never do this
var sql = $"SELECT * FROM Users WHERE Email = '{email}'";
await context.Database.ExecuteSqlRawAsync(sql);
Always do this
// EF Core — parameterized automaticallyvar user = await context.Users.FirstOrDefaultAsync(u => u.Email == email);
// If raw SQL is needed — parameterizedawait context.Database.ExecuteSqlInterpolatedAsync(
$"SELECT * FROM Users WHERE Email = {email}");
// Or explicit parametersawait context.Database.ExecuteSqlRawAsync(
"SELECT * FROM Users WHERE Email = @p0", email);
Checklist:
All queries use EF Core LINQ or parameterized SQL
No string concatenation in any SQL
ExecuteSqlInterpolatedAsync over ExecuteSqlRawAsync
For Event Sourcing: blob keys validated and sanitized
For Event Sourcing: event stream access scoped per tenant/user
4. Authentication & Authorization
// Enforce auth on endpoints
app.MapGet("/api/orders", GetOrders).RequireAuthorization();
// Role-based
app.MapDelete("/api/orders/{id}", DeleteOrder).RequireAuthorization("AdminOnly");
// Policy-based authorization
builder.Services.AddAuthorizationBuilder()
.AddPolicy("AdminOnly", p => p.RequireRole("Admin"))
.AddPolicy("CanManageOrders", p => p.RequireClaim("permission", "orders:manage"));
Dependencies: dotnet list package --vulnerable clean
Flags
Flag
Behavior
--checklist
Print the pre-deployment checklist only
--scan
Run automated scans (secret grep, dotnet list package --vulnerable, Aspire runtime errors)
--full
Full review: checklist + scans + runtime checks + code review of changed files
Aspire Runtime Security Checks (--scan / --full)
If the Aspire AppHost is running, the --scan and --full flags include runtime security checks:
Step 1 — Check for leaked error details:
~/.dotnet/tools/aspire otel logs api --format Json --severity Error -n 20
Scan for responses that leak stack traces, internal paths, or sensitive data in error messages. Production APIs should return Problem Details (RFC 7807) without internal details.
Step 2 — Check for auth failures in logs:
Filter structured logs for 401/403 responses. A high volume may indicate misconfigured auth, missing policies, or endpoints that should require auth but don't.
Step 3 — Check console for security warnings:
~/.dotnet/tools/aspire logs api --format Json -n 50