skills/security/data-protection/SKILL.md
Use when encrypting data at rest with ASP.NET Core Data Protection API.
npx skillsauth add faysilalshareef/dotnet-ai-kit data-protectionInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
// Program.cs — configure data protection
builder.Services.AddDataProtection()
.SetApplicationName("{Company}.{Domain}")
.SetDefaultKeyLifetime(TimeSpan.FromDays(90));
// Injecting and using
public sealed class SensitiveDataService(
IDataProtectionProvider protectionProvider)
{
private readonly IDataProtector _protector =
protectionProvider.CreateProtector("SensitiveData.v1");
public string Protect(string plainText)
=> _protector.Protect(plainText);
public string Unprotect(string protectedText)
=> _protector.Unprotect(protectedText);
}
public sealed class TokenProtectionService(
IDataProtectionProvider provider)
{
// Each purpose creates an isolated encryption scope
private readonly IDataProtector _emailConfirmation =
provider.CreateProtector("EmailConfirmation");
private readonly IDataProtector _passwordReset =
provider.CreateProtector("PasswordReset");
private readonly IDataProtector _apiKeys =
provider.CreateProtector("ApiKeys.v1");
public string ProtectEmailToken(string userId)
=> _emailConfirmation.Protect(userId);
public string UnprotectEmailToken(string token)
=> _emailConfirmation.Unprotect(token);
public string ProtectApiKey(string keyData)
=> _apiKeys.Protect(keyData);
}
public sealed class TimeLimitedTokenService(
IDataProtectionProvider provider)
{
private readonly ITimeLimitedDataProtector _protector =
provider.CreateProtector("TimeLimitedTokens")
.ToTimeLimitedDataProtector();
// Token expires after specified duration
public string CreatePasswordResetToken(Guid userId)
{
var payload = userId.ToString();
return _protector.Protect(
payload,
lifetime: TimeSpan.FromHours(1));
}
public Guid? ValidatePasswordResetToken(string token)
{
try
{
var payload = _protector.Unprotect(token);
return Guid.Parse(payload);
}
catch (CryptographicException)
{
// Token is invalid or expired
return null;
}
}
public string CreateEmailConfirmationToken(string email)
{
return _protector.Protect(
email,
lifetime: TimeSpan.FromDays(7));
}
}
// File system (shared network drive for web farm)
builder.Services.AddDataProtection()
.SetApplicationName("{Company}.{Domain}")
.PersistKeysToFileSystem(
new DirectoryInfo(@"\\server\share\keys"));
// Redis
builder.Services.AddDataProtection()
.SetApplicationName("{Company}.{Domain}")
.PersistKeysToStackExchangeRedis(
ConnectionMultiplexer.Connect(
builder.Configuration.GetConnectionString("Redis")!),
"DataProtection-Keys");
// Azure Blob Storage + Key Vault
builder.Services.AddDataProtection()
.SetApplicationName("{Company}.{Domain}")
.PersistKeysToAzureBlobStorage(blobUri)
.ProtectKeysWithAzureKeyVault(keyIdentifier, credential);
public sealed class EncryptedFieldConverter(
IDataProtector protector) : ValueConverter<string, string>(
v => protector.Protect(v),
v => protector.Unprotect(v))
{
}
// Entity configuration
internal sealed class CustomerConfiguration
: IEntityTypeConfiguration<Customer>
{
private readonly IDataProtector _protector;
public CustomerConfiguration(IDataProtectionProvider provider)
{
_protector = provider.CreateProtector("Customer.SSN");
}
public void Configure(EntityTypeBuilder<Customer> builder)
{
builder.Property(c => c.SocialSecurityNumber)
.HasConversion(
v => _protector.Protect(v),
v => _protector.Unprotect(v))
.HasMaxLength(500); // encrypted value is longer
}
}
// Data protection automatically protects:
// - Authentication cookies
// - Session cookies
// - Antiforgery tokens
// - TempData
// Custom cookie protection
builder.Services.AddAuthentication(
CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Cookie.Name = ".{Company}.Auth";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.Strict;
});
// Additional security hardening
app.UseHsts();
app.UseHttpsRedirection();
app.Use(async (context, next) =>
{
context.Response.Headers.Append(
"X-Content-Type-Options", "nosniff");
context.Response.Headers.Append(
"X-Frame-Options", "DENY");
context.Response.Headers.Append(
"Referrer-Policy", "strict-origin-when-cross-origin");
context.Response.Headers.Append(
"Permissions-Policy",
"camera=(), microphone=(), geolocation=()");
await next();
});
CryptographicException from Unprotect (means tampering or expiry)AddDataProtection in Program.csIDataProtectionProvider or IDataProtector injectionPersistKeysToFileSystem or PersistKeysToStackExchangeRedisCryptographicException handlingToTimeLimitedDataProtector usageProgram.cs with application name| Scenario | Approach | |----------|----------| | Password reset tokens | Time-limited data protection | | API key storage | Purpose-isolated data protector | | Sensitive DB fields | Value converter with data protector | | Distributed web farm | Redis or Azure key storage | | Single server | File system key storage |
data-ai
Use when about to claim work is complete, fixed, passing, or ready — before committing, creating PRs, or moving to the next task. Requires running verification commands and confirming output before making any success claims.
development
Use when encountering any bug, test failure, build error, or unexpected behavior — before proposing fixes or making changes.
development
Use when checkpointing, wrapping up, or handing off an AI-assisted development session.
development
Use when following the Specification-Driven Development lifecycle from plan through ship.