第十九章:安全性與資料保護 (Security & Data Protection)
19.1 引言:安全性的重要性
安全性漏洞可能導致:
- 資料外洩:客戶資料被竊取,面臨法律訴訟與罰款。
- 服務中斷:DDoS 攻擊導致服務無法使用。
- 聲譽損害:客戶失去信任。
根據 OWASP (Open Web Application Security Project),我們將探討最常見的安全威脅及其防範方法。
19.2 授權 (Authorization)
授權決定了「已驗證的使用者」是否有權執行某個操作。
1. 基於權限的授權
ABP 提供了強大的權限系統。
定義權限:
csharp
public class BookStorePermissionDefinitionProvider : PermissionDefinitionProvider
{
public override void Define(IPermissionDefinitionContext context)
{
var bookStoreGroup = context.AddGroup("BookStore", L("Permission:BookStore"));
var booksPermission = bookStoreGroup.AddPermission(
"BookStore.Books",
L("Permission:Books")
);
booksPermission.AddChild("BookStore.Books.Create", L("Permission:Create"));
booksPermission.AddChild("BookStore.Books.Edit", L("Permission:Edit"));
booksPermission.AddChild("BookStore.Books.Delete", L("Permission:Delete"));
}
}使用權限:
csharp
public class BookAppService : ApplicationService
{
[Authorize("BookStore.Books.Create")]
public async Task<BookDto> CreateAsync(CreateBookDto input)
{
var book = new Book(GuidGenerator.Create(), input.Name, input.Price);
await _bookRepository.InsertAsync(book);
return ObjectMapper.Map<Book, BookDto>(book);
}
[Authorize("BookStore.Books.Delete")]
public async Task DeleteAsync(Guid id)
{
await _bookRepository.DeleteAsync(id);
}
}2. 基於策略的授權 (Policy-Based Authorization)
對於複雜的授權邏輯,使用策略。
csharp
public class MinimumAgeRequirement : IAuthorizationRequirement
{
public int MinimumAge { get; }
public MinimumAgeRequirement(int minimumAge) => MinimumAge = minimumAge;
}
public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement>
{
protected override Task HandleRequirementAsync(
AuthorizationHandlerContext context,
MinimumAgeRequirement requirement)
{
var ageClaim = context.User.FindFirst(c => c.Type == "age");
if (ageClaim != null && int.Parse(ageClaim.Value) >= requirement.MinimumAge)
{
context.Succeed(requirement);
}
return Task.CompletedTask;
}
}
// 註冊
services.AddAuthorization(options =>
{
options.AddPolicy("Adult", policy =>
policy.Requirements.Add(new MinimumAgeRequirement(18)));
});
services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>();
// 使用
[Authorize(Policy = "Adult")]
public async Task<AdultContentDto> GetAdultContentAsync() { }19.3 資料加密 (Data Encryption)
1. ASP.NET Core Data Protection API
用於加密敏感資料 (如個人識別資訊)。
配置:
csharp
builder.Services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(@"/var/keys"))
.SetApplicationName("BookStore")
.ProtectKeysWithCertificate(certificate); // 生產環境使用憑證使用:
csharp
public class UserService : ITransientDependency
{
private readonly IDataProtectionProvider _dataProtectionProvider;
private readonly IDataProtector _protector;
public UserService(IDataProtectionProvider dataProtectionProvider)
{
_dataProtectionProvider = dataProtectionProvider;
_protector = _dataProtectionProvider.CreateProtector("UserService.PersonalData");
}
public string EncryptIdCard(string idCard)
{
return _protector.Protect(idCard);
}
public string DecryptIdCard(string encryptedIdCard)
{
return _protector.Unprotect(encryptedIdCard);
}
}2. 在實體中自動加密
使用 EF Core 的 Value Converters。
csharp
public class EncryptedStringConverter : ValueConverter<string, string>
{
public EncryptedStringConverter(IDataProtector protector)
: base(
v => protector.Protect(v),
v => protector.Unprotect(v))
{
}
}
// 在 DbContext 中配置
protected override void OnModelCreating(ModelBuilder builder)
{
var protector = _dataProtectionProvider.CreateProtector("EntityEncryption");
builder.Entity<User>(b =>
{
b.Property(e => e.IdCardNumber)
.HasConversion(new EncryptedStringConverter(protector));
});
}19.4 GDPR 合規 (GDPR Compliance)
GDPR (General Data Protection Regulation) 是歐盟的資料保護法規,但其原則適用於全球。
1. 資料最小化 (Data Minimization)
只收集必要的資料。
2. 同意管理 (Consent Management)
csharp
public class UserConsent : Entity<Guid>
{
public Guid UserId { get; set; }
public string Purpose { get; set; } // "Marketing", "Analytics"
public bool Granted { get; set; }
public DateTime GrantedAt { get; set; }
public string Version { get; set; } // 隱私政策版本
}
public class ConsentService : ITransientDependency
{
private readonly IRepository<UserConsent, Guid> _consentRepository;
public async Task<bool> HasConsentAsync(Guid userId, string purpose)
{
return await _consentRepository.AnyAsync(c =>
c.UserId == userId &&
c.Purpose == purpose &&
c.Granted);
}
public async Task GrantConsentAsync(Guid userId, string purpose, string version)
{
var consent = new UserConsent
{
Id = Guid.NewGuid(),
UserId = userId,
Purpose = purpose,
Granted = true,
GrantedAt = DateTime.UtcNow,
Version = version
};
await _consentRepository.InsertAsync(consent);
}
}3. 資料刪除權 (Right to Erasure)
csharp
public class UserDataDeletionService : ITransientDependency
{
private readonly IRepository<User, Guid> _userRepository;
private readonly IRepository<Order, Guid> _orderRepository;
public async Task DeleteUserDataAsync(Guid userId)
{
var user = await _userRepository.GetAsync(userId);
// 匿名化個人資料
user.Email = $"deleted_{userId}@example.com";
user.PhoneNumber = null;
user.IdCardNumber = null;
user.IsActive = false;
await _userRepository.UpdateAsync(user);
// 匿名化訂單資料
var orders = await _orderRepository.GetListAsync(o => o.CustomerId == userId);
foreach (var order in orders)
{
order.ShippingAddress = null;
await _orderRepository.UpdateAsync(order);
}
}
}19.5 防範 OWASP Top 10
1. Injection (注入攻擊)
威脅:SQL Injection, NoSQL Injection。 防範:使用參數化查詢 (EF Core 預設)。
csharp
// ❌ 危險
var sql = $"SELECT * FROM Books WHERE Title = '{title}'";
var books = _context.Books.FromSqlRaw(sql).ToList();
// ✅ 安全
var books = await _bookRepository.GetListAsync(b => b.Title == title);2. Broken Authentication (身分驗證失效)
威脅:弱密碼、Session 劫持。 防範:
- 強制強密碼政策。
- 使用 HTTPS。
- 實作 Multi-Factor Authentication (MFA)。
csharp
Configure<IdentityOptions>(options =>
{
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequiredLength = 8;
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(15);
});3. Sensitive Data Exposure (敏感資料外洩)
威脅:明文儲存密碼、信用卡號。 防範:
- 使用 HTTPS (TLS 1.2+)。
- 加密敏感欄位 (如前述 Data Protection API)。
- 不要在日誌中記錄敏感資料。
csharp
// 過濾敏感資料
Configure<AbpAuditingOptions>(options =>
{
options.IgnoredTypes.Add(typeof(CreditCard));
});4. XML External Entities (XXE)
威脅:XML 解析器被利用來讀取伺服器檔案。 防範:停用 DTD 處理。
csharp
var settings = new XmlReaderSettings
{
DtdProcessing = DtdProcessing.Prohibit,
XmlResolver = null
};
using var reader = XmlReader.Create(stream, settings);5. Broken Access Control (存取控制失效)
威脅:使用者能存取不屬於他的資料。 防範:在每個操作中驗證權限。
csharp
public async Task<BookDto> GetAsync(Guid id)
{
var book = await _bookRepository.GetAsync(id);
// 確保使用者有權存取此書籍
if (book.OwnerId != CurrentUser.Id && !await IsAdminAsync())
{
throw new AbpAuthorizationException();
}
return ObjectMapper.Map<Book, BookDto>(book);
}6. Security Misconfiguration (安全性設定錯誤)
威脅:預設密碼、錯誤訊息洩漏資訊。 防範:
- 移除預設帳號或強制修改密碼。
- 在生產環境關閉詳細錯誤訊息。
csharp
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}7. Cross-Site Scripting (XSS)
威脅:注入惡意 JavaScript。 防範:Razor 預設會編碼輸出。
html
<!-- ✅ 安全:自動編碼 -->
<p>@Model.UserComment</p>
<!-- ❌ 危險:不編碼 -->
<p>@Html.Raw(Model.UserComment)</p>8. Insecure Deserialization (不安全的反序列化)
威脅:惡意序列化資料導致 RCE (Remote Code Execution)。 防範:不要反序列化不受信任的資料。
9. Using Components with Known Vulnerabilities
威脅:使用有已知漏洞的 NuGet 套件。 防範:定期更新套件。
bash
dotnet list package --vulnerable
dotnet outdated10. Insufficient Logging & Monitoring
威脅:攻擊無法被偵測。 防範:記錄所有安全相關事件。
csharp
public async Task<bool> LoginAsync(string username, string password)
{
try
{
var result = await _signInManager.PasswordSignInAsync(username, password, false, true);
if (result.Succeeded)
{
Logger.LogInformation("User {Username} logged in successfully", username);
}
else
{
Logger.LogWarning("Failed login attempt for user {Username}", username);
}
return result.Succeeded;
}
catch (Exception ex)
{
Logger.LogError(ex, "Login error for user {Username}", username);
throw;
}
}19.6 審計日誌 (Audit Logging)
ABP 自動記錄所有 Application Service 的呼叫。
1. 啟用審計
csharp
Configure<AbpAuditingOptions>(options =>
{
options.IsEnabled = true;
options.IsEnabledForGetRequests = true; // 記錄 GET 請求
options.ApplicationName = "BookStore";
});2. 查詢審計日誌
csharp
public class AuditLogAppService : ApplicationService
{
private readonly IAuditLogRepository _auditLogRepository;
public async Task<List<AuditLogDto>> GetUserAuditLogsAsync(Guid userId, DateTime from, DateTime to)
{
var logs = await _auditLogRepository.GetListAsync(
userId: userId,
startTime: from,
endTime: to
);
return ObjectMapper.Map<List<AuditLog>, List<AuditLogDto>>(logs);
}
}19.7 實戰練習
練習 1:實作權限系統
- 定義完整的權限樹 (至少 10 個權限)。
- 為不同角色分配權限。
- 測試未授權存取會被拒絕。
練習 2:資料加密
- 為
User實體的IdCardNumber欄位實作自動加密。 - 驗證資料庫中儲存的是密文。
練習 3:GDPR 合規
- 實作完整的資料刪除流程。
- 實作資料匯出功能 (Right to Data Portability)。
19.8 總結
安全性是一個持續的過程,而非一次性的任務。
- 授權 確保使用者只能做他們被允許的事。
- 加密 保護敏感資料。
- GDPR 合規保護使用者隱私。
- OWASP Top 10 提供了安全檢查清單。
在下一章,我們將進入 UI 現代化,探討如何整合現代前端框架。
參考資源: