# Security Patterns - Detailed Reference Detailed code examples for security patterns in ASP.NET Core. ## Table of Contents 1. [Authentication with Identity](#authentication-with-identity) 2. [JWT Configuration](#jwt-configuration) 3. [Authorization Policies](#authorization-policies) 4. [Password Hashing](#password-hashing) 5. [Two-Factor Authentication](#two-factor-authentication) 6. [Input Validation](#input-validation) 7. [Rate Limiting](#rate-limiting) 8. [Global Exception Handler](#global-exception-handler) 9. [Audit Logging](#audit-logging) 10. [CORS Configuration](#cors-configuration) --- ## Authentication with Identity ### Register User Command Handler ```csharp /// /// EN: Handler for user registration. /// VI: Handler cho việc đăng ký user. /// public class RegisterUserCommandHandler : IRequestHandler { private readonly UserManager _userManager; private readonly ILogger _logger; public RegisterUserCommandHandler( UserManager userManager, ILogger logger) { _userManager = userManager; _logger = logger; } public async Task Handle( RegisterUserCommand request, CancellationToken cancellationToken) { // EN: Check if user exists / VI: Kiểm tra user đã tồn tại var existingUser = await _userManager.FindByEmailAsync(request.Email); if (existingUser != null) { throw new ConflictException("User with this email already exists"); } // EN: Create new user / VI: Tạo user mới var user = new ApplicationUser { UserName = request.Email, Email = request.Email, FullName = request.FullName, EmailConfirmed = false }; // EN: Identity handles password hashing automatically // VI: Identity tự động hash password var result = await _userManager.CreateAsync(user, request.Password); if (!result.Succeeded) { var errors = string.Join(", ", result.Errors.Select(e => e.Description)); throw new ValidationException(errors); } // EN: Assign default role / VI: Gán role mặc định await _userManager.AddToRoleAsync(user, "User"); _logger.LogInformation( "EN: User registered successfully / VI: Đăng ký user thành công: {Email}", user.Email); return new RegisterResult(user.Id, user.Email!); } } ``` --- ## JWT Configuration ### Program.cs Configuration ```csharp // EN: Configure JWT authentication / VI: Cấu hình JWT authentication builder.Services.AddAuthentication(options => { options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; }) .AddJwtBearer(options => { options.Authority = builder.Configuration["IdentityServer:Authority"]; options.Audience = builder.Configuration["IdentityServer:Audience"]; options.RequireHttpsMetadata = !builder.Environment.IsDevelopment(); options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ClockSkew = TimeSpan.FromMinutes(5) }; options.Events = new JwtBearerEvents { OnAuthenticationFailed = context => { var logger = context.HttpContext.RequestServices .GetRequiredService>(); logger.LogWarning( "EN: JWT authentication failed / VI: JWT authentication thất bại: {Error}", context.Exception.Message); return Task.CompletedTask; } }; }); ``` --- ## Authorization Policies ### Configure Policies ```csharp // EN: Configure authorization policies / VI: Cấu hình authorization policies builder.Services.AddAuthorization(options => { // EN: Role-based policy / VI: Policy dựa trên role options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin")); // EN: Multiple roles / VI: Nhiều roles options.AddPolicy("CanManageUsers", policy => policy.RequireRole("Admin", "Manager")); // EN: Claim-based policy / VI: Policy dựa trên claim options.AddPolicy("PremiumUser", policy => policy.RequireClaim("subscription", "premium")); // EN: Custom requirement / VI: Requirement tùy chỉnh options.AddPolicy("ResourceOwner", policy => policy.AddRequirements(new ResourceOwnerRequirement())); }); /// /// EN: Custom authorization requirement. /// VI: Authorization requirement tùy chỉnh. /// public class ResourceOwnerRequirement : IAuthorizationRequirement { } public class ResourceOwnerHandler : AuthorizationHandler { protected override Task HandleRequirementAsync( AuthorizationHandlerContext context, ResourceOwnerRequirement requirement) { var userId = context.User.FindFirst("sub")?.Value; var resourceOwnerId = context.Resource as string; if (userId == resourceOwnerId) { context.Succeed(requirement); } return Task.CompletedTask; } } ``` --- ## Password Hashing ### ASP.NET Core Identity (Automatic) ```csharp /// /// EN: Identity uses PBKDF2 with HMAC-SHA256 by default. /// VI: Identity sử dụng PBKDF2 với HMAC-SHA256 mặc định. /// // EN: Creating user with password (auto-hashed) // VI: Tạo user với password (tự động hash) await _userManager.CreateAsync(user, password); // EN: Verify password / VI: Xác minh password var isValid = await _userManager.CheckPasswordAsync(user, password); // EN: Change password / VI: Đổi password await _userManager.ChangePasswordAsync(user, currentPassword, newPassword); // EN: Reset password with token / VI: Reset password với token var token = await _userManager.GeneratePasswordResetTokenAsync(user); await _userManager.ResetPasswordAsync(user, token, newPassword); ``` ### Custom Password Hasher (If Needed) ```csharp /// /// EN: Configure password hasher options. /// VI: Cấu hình password hasher options. /// builder.Services.Configure(options => { // EN: Iteration count (higher = more secure but slower) // VI: Số lần lặp (cao hơn = bảo mật hơn nhưng chậm hơn) options.IterationCount = 100000; }); ``` --- ## Two-Factor Authentication ### Enable 2FA Command Handler ```csharp /// /// EN: Handler for enabling 2FA. /// VI: Handler cho việc bật 2FA. /// public class Enable2FACommandHandler : IRequestHandler { private readonly UserManager _userManager; private const string AuthenticatorUriFormat = "otpauth://totp/{0}:{1}?secret={2}&issuer={0}&digits=6"; public async Task Handle( Enable2FACommand request, CancellationToken ct) { var user = await _userManager.FindByIdAsync(request.UserId.ToString()); if (user == null) throw new NotFoundException("User not found"); // EN: Check if 2FA already enabled / VI: Kiểm tra 2FA đã bật chưa if (await _userManager.GetTwoFactorEnabledAsync(user)) throw new InvalidOperationException("2FA is already enabled"); // EN: Generate authenticator key / VI: Tạo key authenticator await _userManager.ResetAuthenticatorKeyAsync(user); var key = await _userManager.GetAuthenticatorKeyAsync(user); // EN: Generate QR code / VI: Tạo QR code var uri = string.Format( AuthenticatorUriFormat, "GoodGo", user.Email, key); var qrCode = GenerateQrCode(uri); // EN: Generate recovery codes / VI: Tạo mã khôi phục var recoveryCodes = await _userManager .GenerateNewTwoFactorRecoveryCodesAsync(user, 10); return new Enable2FAResult { QrCodeBase64 = qrCode, ManualEntryKey = key!, RecoveryCodes = recoveryCodes?.ToArray() ?? Array.Empty() }; } private string GenerateQrCode(string uri) { using var qrGenerator = new QRCodeGenerator(); using var qrData = qrGenerator.CreateQrCode(uri, QRCodeGenerator.ECCLevel.Q); using var qrCode = new PngByteQRCode(qrData); var qrBytes = qrCode.GetGraphic(20); return Convert.ToBase64String(qrBytes); } } ``` ### Verify 2FA Code ```csharp /// /// EN: Verify TOTP code during login. /// VI: Xác minh mã TOTP khi đăng nhập. /// public async Task VerifyTwoFactorCodeAsync( ApplicationUser user, string code) { // EN: Verify TOTP code / VI: Xác minh mã TOTP var isValid = await _userManager .VerifyTwoFactorTokenAsync( user, _userManager.Options.Tokens.AuthenticatorTokenProvider, code); if (!isValid) { // EN: Try recovery code / VI: Thử mã khôi phục var result = await _userManager .RedeemTwoFactorRecoveryCodeAsync(user, code); return result.Succeeded; } return true; } ``` --- ## Input Validation ### FluentValidation Setup ```csharp // EN: Register FluentValidation / VI: Đăng ký FluentValidation builder.Services.AddValidatorsFromAssemblyContaining(); builder.Services.AddFluentValidationAutoValidation(); /// /// EN: Validator for register command. /// VI: Validator cho register command. /// public class RegisterUserCommandValidator : AbstractValidator { public RegisterUserCommandValidator() { RuleFor(x => x.Email) .NotEmpty().WithMessage("Email is required") .EmailAddress().WithMessage("Invalid email format"); RuleFor(x => x.Password) .NotEmpty().WithMessage("Password is required") .MinimumLength(8).WithMessage("Password must be at least 8 characters") .Matches("[A-Z]").WithMessage("Password must contain uppercase letter") .Matches("[a-z]").WithMessage("Password must contain lowercase letter") .Matches("[0-9]").WithMessage("Password must contain digit") .Matches("[^a-zA-Z0-9]").WithMessage("Password must contain special character"); RuleFor(x => x.FullName) .MaximumLength(100).WithMessage("Name cannot exceed 100 characters"); } } ``` --- ## Rate Limiting ### ASP.NET Core Rate Limiting ```csharp // EN: Configure rate limiting / VI: Cấu hình rate limiting builder.Services.AddRateLimiter(options => { // EN: Standard rate limit / VI: Rate limit chuẩn options.AddFixedWindowLimiter("standard", opt => { opt.PermitLimit = 100; opt.Window = TimeSpan.FromMinutes(15); opt.QueueProcessingOrder = QueueProcessingOrder.OldestFirst; opt.QueueLimit = 5; }); // EN: Strict rate limit for sensitive operations // VI: Rate limit nghiêm ngặt cho operations nhạy cảm options.AddFixedWindowLimiter("strict", opt => { opt.PermitLimit = 10; opt.Window = TimeSpan.FromHours(1); }); // EN: Login rate limit / VI: Rate limit cho login options.AddSlidingWindowLimiter("login", opt => { opt.PermitLimit = 5; opt.Window = TimeSpan.FromMinutes(15); opt.SegmentsPerWindow = 3; }); options.OnRejected = async (context, token) => { context.HttpContext.Response.StatusCode = 429; await context.HttpContext.Response.WriteAsJsonAsync( ApiResponse.Fail("RATE_LIMITED", "Too many requests"), token); }; }); // EN: Apply rate limiting / VI: Áp dụng rate limiting app.UseRateLimiter(); // EN: Use on specific endpoints / VI: Dùng cho endpoints cụ thể [EnableRateLimiting("login")] [HttpPost("login")] public async Task Login(...) { } ``` --- ## Global Exception Handler ### Exception Handler Middleware ```csharp /// /// EN: Global exception handler middleware. /// VI: Middleware xử lý exception toàn cục. /// public class ExceptionHandlerMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; public ExceptionHandlerMiddleware( RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } public async Task InvokeAsync(HttpContext context) { try { await _next(context); } catch (Exception ex) { await HandleExceptionAsync(context, ex); } } private async Task HandleExceptionAsync(HttpContext context, Exception ex) { // EN: Log full error details / VI: Log chi tiết lỗi đầy đủ _logger.LogError(ex, "EN: Unhandled exception / VI: Exception không xử lý: {Path}", context.Request.Path); var (statusCode, message) = ex switch { ValidationException ve => (400, ve.Message), UnauthorizedAccessException => (401, "Unauthorized"), NotFoundException => (404, "Resource not found"), ConflictException => (409, "Resource already exists"), _ => (500, "An unexpected error occurred") }; context.Response.StatusCode = statusCode; context.Response.ContentType = "application/json"; // EN: Don't expose internal errors / VI: Không lộ lỗi internal var response = ApiResponse.Fail( statusCode == 500 ? "INTERNAL_ERROR" : "ERROR", message); await context.Response.WriteAsJsonAsync(response); } } // EN: Register in Program.cs / VI: Đăng ký trong Program.cs app.UseMiddleware(); ``` --- ## Audit Logging ### Audit Service ```csharp /// /// EN: Service for audit logging. /// VI: Service cho audit logging. /// public interface IAuditService { Task LogSecurityEventAsync( string eventType, Guid? userId, object? details, HttpContext? context = null); } public class AuditService : IAuditService { private readonly ApplicationDbContext _db; private readonly ILogger _logger; public async Task LogSecurityEventAsync( string eventType, Guid? userId, object? details, HttpContext? context = null) { var sanitizedDetails = SanitizeDetails(details); var auditLog = new AuditLog { EventType = eventType, UserId = userId, Details = JsonSerializer.Serialize(sanitizedDetails), IpAddress = context?.Connection.RemoteIpAddress?.ToString(), UserAgent = context?.Request.Headers.UserAgent.FirstOrDefault(), Timestamp = DateTime.UtcNow }; _db.AuditLogs.Add(auditLog); await _db.SaveChangesAsync(); _logger.LogInformation( "EN: Security event / VI: Sự kiện bảo mật: {EventType} UserId: {UserId}", eventType, userId); } private object? SanitizeDetails(object? details) { if (details == null) return null; var json = JsonSerializer.Serialize(details); var dict = JsonSerializer.Deserialize>(json); var sensitiveKeys = new[] { "password", "token", "secret", "key" }; foreach (var key in sensitiveKeys) { if (dict?.ContainsKey(key) == true) dict[key] = "[REDACTED]"; } return dict; } } ``` --- ## CORS Configuration ```csharp // EN: Configure CORS / VI: Cấu hình CORS var allowedOrigins = builder.Configuration .GetSection("Cors:AllowedOrigins") .Get() ?? Array.Empty(); builder.Services.AddCors(options => { options.AddPolicy("Default", policy => { policy.WithOrigins(allowedOrigins) .AllowAnyMethod() .AllowAnyHeader() .AllowCredentials() .SetPreflightMaxAge(TimeSpan.FromHours(24)); }); }); // EN: Apply CORS / VI: Áp dụng CORS app.UseCors("Default"); ``` --- ## Resources / Tài Nguyên - [ASP.NET Core Identity](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity) - [Duende IdentityServer](https://docs.duendesoftware.com/) - [FluentValidation](https://docs.fluentvalidation.net/) - [OWASP Top 10](https://owasp.org/www-project-top-ten/)