ASP.NET Identity

這就像保護寶藏的三個基本原則,缺一不可。任何一個環節出錯,你的寶藏(資料)都可能面臨風險。

機密性 (Confidentiality)

說書人模式: 只有擁有鑰匙的人才能打開寶箱。確保敏感資料,例如個人身份資訊(PII)、財務記錄或商業機密,只被經過授權的人或系統讀取。

生活案例: 你的網路銀行帳戶,只有你知道密碼才能登入查看餘額和交易紀錄,銀行行員也無法隨意窺探。這就是機密性。

技術實現: 強式加密(傳輸中與靜止時)、嚴格的身份驗證機制、基於角色的存取控制 (RBAC)、以及存取權限最小化原則(Principle of Least Privilege)。

完整性 (Integrity)

說書人模式: 確保寶箱裡的地圖沒被任何人偷偷修改過。保護資料在儲存或傳輸過程中不被未經授權地竄改,維持其準確性與可信度。

生活案例: 你轉帳 1000 元給朋友,你希望這筆交易紀錄就是 1000 元,而不是在中途被駭客改成 10000 元或被竄改成轉給別人。這就是完整性。

技術實現: 雜湊演算法 (Hashing,如 SHA-256) 用於驗證檔案或密碼是否被修改、數位簽章 (Digital Signatures) 用於確認來源與內容未被變動、定期資料校驗與稽核軌跡。

可用性 (Availability)

說書人模式: 當你需要寶藏地圖時,它一定在那裡,不會不見或被鎖起來。確保授權使用者在需要時,隨時都能順利存取到資料與服務。

生活案例: 當你想上網購物時,購物網站必須是正常運作的,不能因為駭客的攻擊(如 DDoS 阻斷服務攻擊)而癱瘓。這就是可用性。

技術實現: 系統備份與災難恢復計畫、負載平衡以分散流量、防火牆與入侵防禦系統來抵擋攻擊、充足的硬體資源。

把你的應用程式想像成一棟重要的大樓,你需要這些環環相扣的設施來建立一個縱深防禦體系。

防火牆 (Firewalls) – 大樓警衛

  • 功能: 站在大樓門口,根據訪客名單(預設規則)決定誰可以進出。它可以是硬體設備或軟體,主要功能是「預防」,在網路邊界阻擋已知的惡意流量或未經授權的外部連線。
  • 深入了解: 防火牆分為「無狀態」和「有狀態」檢查。無狀態防火牆像個健忘的警衛,只看單一封包決定放行或阻擋;有狀態防火牆則會記憶整個連線的上下文,安全性更高。

入侵偵測/防禦系統 (IDS/IPS) – 監視器與行動保全

  • IDS (偵測): 就像遍佈大樓的監視器和警報系統,它持續監控網路流量,如果發現可疑行為(如符合已知攻擊特徵的流量),就會立刻發出警報,通知管理員。它只看不動手。
  • IPS (防禦): 這是更進階的行動保全,它不僅能偵測到可疑行為,還能「主動」採取行動,例如直接阻斷該連線,將威脅扼殺在搖籃中。

TLS/SSL – 有上鎖的郵件通道

  • 功能: 當你要寄送一封機密信件時,你會用一個特殊的加密通道來傳送。這就是 HTTPS 背後的技術,它透過「交握 (Handshake)」過程,在你的瀏覽器和網站伺-服器之間建立一個安全的加密連線,確保中間沒人能偷看或竄改傳輸的內容。這同時保護了「機密性」和「完整性」。

VPN (虛擬私人網路) – 專屬秘密通道

  • 功能: 不只是保護單一的連線,而是為你整個裝置(電腦、手機)建立一條從你所在地直通另一個私人網路的秘密地下通道。所有進出你裝置的網路流量都會被加密並通過這個隧道,非常適合在公共 Wi-Fi 環境下或遠端工作時使用,能有效隱藏你的真實 IP 位址。

AES (對稱加密) – 同一把鑰匙的保險箱

  • 原理: 加密和解密都使用同一把密鑰。它是一種區塊加密法,會將資料切成固定大小的區塊(如 128 位元)進行加密。
  • 優點: 速度極快,計算效率高,非常適合用來加密大量的靜態資料,例如整個資料庫或大型檔案。
  • 缺點: 密鑰的分發與管理是最大的挑戰。如果密鑰在傳輸過程中被攔截,那加密就形同虛設。

RSA (非對稱加密) – 公開的信箱與私人的鑰匙

  • 原理: 使用一對數學相關的鑰匙:公鑰和私鑰。公鑰可以公開散佈,任何人都能用它來加密資料;但只有持有對應私鑰的人才能解密。
  • 優點: 安全性極高,完美解決了對稱加密的密鑰交換問題。
  • 缺點: 加解密速度比 AES 慢上數百甚至數千倍。
  • 常見應用: 通常不會用來加密大量資料,而是用在「數位簽章」(用私鑰簽名,大家用公鑰驗證簽名真偽)和「金鑰交換」(用 RSA 安全地交換 AES 的對稱密鑰)等場景。

資料遮罩 (Data Masking) – 戴上馬賽克

  • 目的: 在不改變原始資料庫的情況下,即時地對敏感資料進行部分隱藏或替換,常用於客服或測試環境。
  • 範例: 客服人員在系統中看到的信用卡號可能是 **** **** **** 1234,電話號碼顯示為 (XXX) XXX-7890。他們能看到足夠的資訊來完成工作,但無法取得完整的敏感資料。

資料混淆 (Data Obfuscation) – 讓它變成火星文

  • 目的: 故意讓資料變得難以理解或無法逆向工程,通常會改變原始資料。
  • 範例: 「權杖化 (Tokenization)」是一種常見的混淆技術,例如用一個隨機無意義的 Token tok_9XJ67P 來代替真實的信用卡號儲存在資料庫中。真實卡號則被安全地存放在另一個隔離的系統(稱為 Vault)。

A01 – 損壞的存取控制 (Broken Access Control): 權限控管的漏洞。

  • 如何發生: 攻擊者只需修改 URL 中的 ID (/user/123 改成 /user/124),就能看到別人的資料。或是普通使用者發現能直接存取管理員頁面 (/admin/dashboard)。
  • 如何防禦: 在後端對每個請求都進行嚴格的權限檢查。在 ASP.NET Core 中,善用 [Authorize] 屬性,並且在處理特定資源時,要驗證當前使用者是否為該資源的擁有者。

A02 – 加密失敗 (Cryptographic Failures): 與加密相關的失敗。

  • 如何發生: 密碼用明文或弱雜湊 (如 MD5) 儲存;傳輸敏感資料時未使用 HTTPS;加密金鑰被硬編碼在程式碼中。
  • 如何防禦: 使用強雜湊演算法(ASP.NET Identity 已內建);強制全站 HTTPS;使用安全的金鑰管理系統(如 Azure Key Vault)。

A03 – 注入 (Injection): 將惡意程式碼注入系統。

  • 如何發生: 最常見的是 SQL Injection,攻擊者在登入框輸入 ‘ OR ‘1’=’1 來繞過驗證。
  • 如何防禦: 永遠不要自己組合 SQL 查詢字串。使用 ORM 框架如 Entity Framework Core,它會自動使用參數化查詢,從根本上杜絕 SQL Injection。同時要對所有使用者輸入進行驗證。

A04 – 不安全設計 (Insecure Design): 架構設計階段的缺陷。

  • 如何發生: 設計時就沒有考慮到威脅模型,例如轉帳流程缺乏多重驗證,或密碼重設流程可以被輕易繞過。
  • 如何防禦: 在開發初期就導入安全設計原則(Security by Design),進行威脅建模(Threat Modeling),從源頭思考可能的攻擊路徑並加以防範。

A05 – 安全性錯誤配置 (Security Misconfiguration): 伺服器或框架的設定不當。

  • 如何發生: 雲端儲存(如 S3)設定為公開;使用了框架的預設帳號密碼;在生產環境中開啟了詳細的錯誤訊息頁面。
  • 如何防禦: 最小化攻擊面,關閉不必要的功能與埠口;建立標準化的強化設定流程;定期進行安全性掃描。

A06 – 易受攻擊和過時的元件 (Vulnerable and Outdated Components): 使用了有已知漏洞的第三方套件。

  • 如何發生: 你的應用程式依賴了一個舊版的 Newtonsoft.Json 或 log4j,而這些版本有已知的安全性漏洞。
  • 如何防禦: 定期使用工具(如 GitHub Dependabot 或 dotnet list package –vulnerable)掃描專案的依賴項,並及時更新到安全的版本。

A07 – 身份識別與驗證失敗 (Identification and Authentication Failures): 登入與會話管理機制的漏洞。

  • 如何發生: 允許 123456 這種弱密碼;登入失敗次數沒有限制,讓駭客可以暴力破解;Session ID 在登出後未失效。
  • 如何防禦: 實施強密碼策略;設定帳戶鎖定機制;在使用者登入或權限變更時,重新產生 Session ID。

A08 – 軟體和數據完整性失敗 (Software and Data Integrity Failures): 缺乏對軟體或資料來源的完整性驗證。

  • 如何發生: 應用程式的自動更新機制從一個不安全的來源下載更新檔,且沒有驗證其數位簽章,導致攻擊者可以植入惡意程式碼。
  • 如何防禦: 確保所有依賴項都來自可信的來源;對重要的資料交換和軟體更新,使用數位簽章進行完整性校驗。

A09 – 安全記錄和監控失敗 (Security Logging and Monitoring Failures): 缺乏足夠的日誌和監控。

  • 如何發生: 系統沒有記錄重要的安全事件(如登入失敗、權限變更),或是有記錄但從來沒人看。當攻擊發生時,完全無從追查。
  • 如何防禦: 記錄所有關鍵的驗證與授權事件;將日誌集中管理;設定即時警報,當偵測到異常活動時能立即通知相關人員。

A10 – 伺服器端請求偽造 (SSRF): 誘騙伺服器發出惡意請求。

  • 如何發生: 攻擊者提供一個 URL,誘騙你的後端伺服器去請求這個 URL。如果這個 URL 指向內部網路的資源(如 http://192.168.1.10/admin),就可能繞過防火牆,竊取內部敏感資料。
  • 如何防禦: 建立一個允許清單(Allow List),只允許伺服器向預先核准的網域發出請求;嚴格驗證所有由使用者提供的 URL。

使用者身份驗證 (User Authentication):

  • 這是確認「你是誰」的過程。Identity 內建了對帳號密碼登入的完整支援,同時也提供了擴充點讓你輕鬆整合多因素驗證 (MFA,如手機驗證碼) 和第三方社交登入(OAuth 2.0,如 Google, Facebook)。

授權和角色管理 (Authorization and Role Management):

  • 這是確認「你能做什麼」的過程。一旦使用者通過驗證,你可以根據他們被指派的角色(如 “Admin”, “User”, “Manager”)或特定的宣告(Claim,如 “CanEditPosts”)來精細地控制他們對系統各個功能的存取權限。

整合與模組化 (Integration and Modularity):

  • 它與 ASP.NET Core 的生態系統(如 MVC, Razor Pages, Web API)無縫整合。你可以使用它的預設 UI,也可以完全客製化所有介面,給予你最大的彈性。

使用 Entity Framework Core 進行資料儲存:

  • 它預設使用 EF Core 來將所有安全性相關的資料(使用者資訊、已安全雜湊的密碼、角色、宣告等)儲存在你選擇的資料庫中(如 SQL Server, PostgreSQL)。這意味著你會在資料庫中看到如 AspNetUsers, AspNetRoles, AspNetUserRoles 等資料表。

1.UserManager<TUser>:

  • 職責: 使用者相關操作的總管。
  • 常用方法: CreateAsync, FindByIdAsync, FindByEmailAsync, UpdateAsync, DeleteAsync, AddToRoleAsync, CheckPasswordAsync。所有你需要對使用者進行的 CRUD 操作,幾乎都由它包辦。

2.SignInManager<TUser>:

  • 職責: 專門處理使用者登入登出的流程。
  • 常用方法: PasswordSignInAsync (核心的密碼驗證與登入方法), SignInAsync (手動登入使用者), SignOutAsync (登出)。它還負責處理 cookie 的建立與銷毀。

3.RoleManager<TRole>:

  • 職責: 角色相關操作的管理者。
  • 常用方法: CreateAsync, RoleExistsAsync, FindByNameAsync, UpdateAsync, DeleteAsync。所有角色的生命週期都由它管理。

# IdentityDbContext:

  • 職責: Entity Framework Core 的資料庫上下文,是你應用程式程式碼與後端資料庫之間溝通的橋樑。它定義了 Identity 框架所需的資料庫結構。

可以選擇兩種方式來設定 Identity:

選項 A: 使用 Visual Studio 的 Scaffolding 工具 (適合 Web App/MVC)

  1. 在專案上按右鍵 -> 「新增」 -> 「新增 Scaffolding 項目…」。
  2. 選擇「Identity」,工具會引導你選擇要覆寫的頁面(如登入、註冊),並自動幫你安裝所有必要的套件和設定。這是最快的方式,非常適合初學者。

選項 B: 手動安裝 (適合 Web API 或想完全客製化)
# 建立一個新的 MVC 專案
dotnet new mvc -n MySecureApp
cd MySecureApp

# — 安裝必要的 NuGet 套件 —

# Identity 核心與 EF Core 的整合,這是基礎
dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore

# Identity 的預設 UI (如果需要自己產生或客製化頁面時會用到)
dotnet add package Microsoft.AspNetCore.Identity.UI

# 資料庫提供者 (這裡是 SQL Server,可換成 PostgreSQL, MySQL 等)
dotnet add package Microsoft.EntityFrameworkCore.SqlServer

# EF Core 資料庫遷移工具,用來產生和更新資料庫結構
dotnet add package Microsoft.EntityFrameworkCore.Tools

# (可選) 如果你想先用記憶體資料庫快速測試
# dotnet add package Microsoft.EntityFrameworkCore.InMemory

# 還原所有套件,確保依賴都已下載
dotnet restore

建立 ApplicationDbContext.cs:
這個類別繼承自 IdentityDbContext,是你應用程式的資料庫上下文。你也可以在這裡加入你自己的 DbSet 來管理應用程式的其他資料表。

在 Program.cs 中註冊服務:
這是整個設定的核心,告訴 ASP.NET Core 如何設定資料庫連線、如何設定 Identity 的各種選項。

Program.cs

// 1. 從 appsettings.json 取得資料庫連線字串
var connectionString = builder.Configuration.GetConnectionString(“DefaultConnection”);

// 2. 註冊 DbContext 服務,並指定使用 SQL Server
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));

// 3. 註冊 Identity 服務
builder.Services.AddIdentity<IdentityUser, IdentityRole>(options => {
    // — 在這裡可以設定 Identity 的各種選項 —
    // 密碼強度設定

    options.Password.RequireDigit = true; // 需要數字
    options.Password.RequiredLength = 8;  // 最小長度 8
    options.Password.RequireNonAlphanumeric = true; // 需要特殊符號
    options.Password.RequireUppercase = true; // 需要大寫字母
    options.Password.RequireLowercase = true; // 需要小寫字母

    // 帳號鎖定設定
    options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5); // 鎖定 5 分鐘
    options.Lockout.MaxFailedAccessAttempts = 5; // 失敗 5 次後鎖定

    // 使用者設定
    options.User.RequireUniqueEmail = true; // Email 必須是唯一的

    // 登入設定
    options.SignIn.RequireConfirmedAccount = true; // 需要驗證 Email 才能登入
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders(); // 加入AddDefaultTokenProviders(),用於密碼重設、Email 驗證等功能

這部分通常會建立一個 AccountController,並搭配 RegisterViewModel 和 LoginViewModel 來接收和驗證使用者從前端表單輸入的資料。

註冊核心邏輯:

  • 使用注入的 UserManager 服務,呼叫 _userManager.CreateAsync(user, model.Password)。
  • ASP.NET Identity 會自動且安全地處理密碼雜湊,將其轉換成一長串無法逆向的字串後,才存入資料庫。你絕對不能手動處理密碼。

登入核心邏輯:

  • 使用注入的 SignInManager 服務,呼叫 _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: true)。

這個方法會自動處理密碼比對(將使用者輸入的密碼雜湊後與資料庫中的雜湊值比對)、處理登入失敗次數以及帳號鎖定邏輯。

1.建立角色:

  • 注入 RoleManager<IdentityRole>。
  • 在應用程式啟動時,可以寫一個初始化程序來檢查並建立必要的角色,如 “Admin” 和 “User”。
  • if (!await _roleManager.RoleExistsAsync(“Admin”)) { await _roleManager.CreateAsync(new IdentityRole(“Admin”)); }

2.為使用者指派角色:

  • 注入 UserManager<IdentityUser>。
  • 在使用者註冊後,或是在管理員後台,先用 _userManager.FindByEmailAsync(…) 找到使用者物件。
  • 然後呼叫 _userManager.AddToRoleAsync(user, “Admin”) 將使用者加入指定的角色。

3.保護資源:

  • 在 Controller 或 Action 方法上加上 [Authorize] 屬性
  • 限制只有特定角色能存取:
    // 只有 Admin 或 Manager 角色可以進入
    [Authorize(Roles = “Admin,Manager”)]
    public IActionResult ManagementDashboard()
    {
        return View();
    }

4.限制只有登入者能存取:

  • [Authorize]
    public IActionResult UserProfile()
    {
        return View();
    }

當你的後端是 Web API,前端是 SPA (如 React, Vue) 或手機 App 時,傳統的 Cookie 驗證方式不再適用,JWT 便是目前的業界標準。

安裝 JWT 相關套件:

dotnet add package System.IdentityModel.Tokens.Jwt
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
dotnet add package System.IdentityModel.Tokens.Jwt

1.JWT 結構 (Header, Payload, Signature):

  • Header (標頭): 內含演算法(alg,如 HS256)和權杖類型(typ,即 JWT)。
  • Payload (酬載): 存放一組稱為「宣告 (Claims)」的資訊,如使用者 ID (sub)、名稱 (name)、角色 (role) 和過期時間 (exp)。
    •  {“sub”: “1234567890”, “name”: “John Doe”, “role”: “admin”, “exp”: 1616239022}
    • 注意:Payload 是經過 Base64 編碼而已,並非加密,所以絕對不要放密碼等機密資料!
  • Signature (簽章): 這是安全性的關鍵。它會將編碼後的 Header 和 Payload,加上一個只有伺服器知道的秘密金鑰 (Secret Key),再用 Header 中指定的演算法進行加密。任何人只要竄改了 Header 或 Payload,簽章就會驗證失敗。
    • SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

2.在 Program.cs 設定 JWT 驗證:

  • 你需要告訴應用程式如何驗證收到的 JWT。這包括設定誰是發行者 (Issuer)、誰是接收者 (Audience) 以及最重要的——用來驗證簽章的金鑰。

3.產生與發行 Token:

  • 當使用者透過 API 登入成功後,伺服器會根據使用者資訊產生一個 JWT 並回傳給客戶端。
  • 客戶端(前端)需要將這個 Token 儲存起來(通常在 localStorage 或 sessionStorage),之後的每次 API 請求,都必須在 HTTP Header 的 Authorization 欄位中附上這個 Token,格式為 Bearer eyJhbGciOiJIUzI1Ni…。
  • 伺服器收到請求後,會用設定好的金鑰來驗證 Token 的簽章是否有效、是否過期,以此來確認使用者的身份。

安全的登入/註冊系統:

  • 建立美化的登入與註冊表單,提供良好的使用者體驗,遵循 OWASP Top 10 指南。
  • 輸入驗證:
    • 前端驗證: 使用 JavaScript 進行基本格式驗證,提升體驗。
    • 後端驗證: 在 ViewModel 中使用 Data Annotations ([Required], [EmailAddress], [StringLength]),並在 Controller 中檢查 ModelState.IsValid,這是最後一道防線。
    • 防止注入: 使用 Entity Framework Core 來確保所有資料庫操作都是參數化的,從而防禦 SQL 注入。對顯示的內容進行 HTML 編碼(Razor 預設會做)來防禦 XSS。
    • 透過刪除惡意字元並確保資料完整性來驗證使用者輸入。
    • 只允許字母、數字以及 @、#、$。
  • 密碼安全:
    • 遵循 Program.cs 中設定的強密碼策略。
    • 使用 ASP.NET Identity 內建的密碼雜湊機制 (預設使用 PBKDF2)。
    • 或使用 bcrypt 或 Argon2 對密碼進行雜湊處理 。
  • 傳輸安全:
    • 在 Program.cs 中設定 HSTS (HTTP Strict Transport Security) 來強制全站使用 HTTPS。

角色型權限管理 (RBAC):

  • 建立一個只有 Admin 角色才能存取的管理儀表板頁面 (AdminController)。
  • 在儀表板上,實作讀取並顯示目前系統中的所有使用者及其所屬角色的功能。
  • 建立一個所有已登入使用者都能看到的通用歡迎頁面或個人資料頁。

API 安全性 (使用 JWT):

  • 實作一個 /api/auth/login 端點,在驗證成功後回傳 JWT。
  • 實作 Token 的生命週期管理:
    • Access Token (存取權杖): 生命週期短(例如 15 分鐘),用於存取受保護的資源。
    • Refresh Token (刷新權杖): 生命週期長(例如 7 天),儲存在資料庫中,用於在 Access Token 過期後,安全地獲取一個新的 Access Token。
  • 建立授權策略(Policy-based Authorization),例如定義一個 “IsAdult” 策略,來保護特定的 API 端點。

單元/整合測試:

  • 撰寫測試來模擬無效的輸入,確保驗證邏輯正常運作。
  • 撰寫測試來模擬對受保護端點的未授權存取,確保會回傳 401 Unauthorized 或 403 Forbidden。

手動滲透測試:

  • 以不同角色(管理員、普通用戶、未登入訪客)登入,手動測試 URL,確保權限控管無誤。
  • 嘗試使用已過期的 JWT Token 存取 API,確保請求被拒絕。
  • 嘗試在輸入框中輸入 <script>alert(‘XSS’)</script> 等腳本,確保內容被正確編碼,腳本不會執行。

發佈留言