Cookie 是 ASP.NET 在用戶端儲存少量資料的經典機制,常用於記住登入狀態、用戶偏好、跨頁面傳遞簡單參數。本文詳細說明 `HttpCookie` 類別操作、編碼處理、安全性屬性、SameSite 政策與現代替代方案。
核心操作:建立、寫入、讀取、刪除
1. 建立與寫入 Cookie (伺服器 → 用戶端)
// 基本寫入
HttpCookie cookie = new HttpCookie("UserPreferences");
cookie.Value = "theme=dark&lang=zh-TW&fontSize=14";
cookie.Expires = DateTime.Now.AddDays(30); // 30 天後過期
Response.Cookies.Add(cookie);
// 進階安全設定 (推薦生產環境)
HttpCookie secureCookie = new HttpCookie("AuthToken", encryptedToken);
secureCookie.Expires = DateTime.Now.AddHours(2);
secureCookie.HttpOnly = true; // 防止 JavaScript 存取 (防 XSS)
secureCookie.Secure = true; // 僅 HTTPS 傳輸 (需 SSL)
secureCookie.SameSite = SameSiteMode.Strict; // 防 CSRF (ASP.NET 4.7.2+)
secureCookie.Domain = ".example.com"; // 跨子域共享 (可選)
Response.Cookies.Add(secureCookie);
2. 讀取 Cookie (用戶端 → 伺服器)
// 安全讀取模式
string GetCookieValue(string name, string defaultValue = "")
{
HttpCookie cookie = Request.Cookies[name];
if (cookie != null && !string.IsNullOrEmpty(cookie.Value))
{
return HttpUtility.UrlDecode(cookie.Value); // 解碼
}
return defaultValue;
}
// 使用範例
string theme = GetCookieValue("UserPreferences", "theme=light"); // 解析自行處理
string authToken = GetCookieValue("AuthToken");
3. 刪除 Cookie (設定過期時間為過去)
// 標準刪除方式
if (Request.Cookies["UserPreferences"] != null)
{
HttpCookie cookie = new HttpCookie("UserPreferences");
cookie.Expires = DateTime.Now.AddDays(-1); // 過去時間
cookie.Path = "/"; // 必須與建立時相同
Response.Cookies.Add(cookie);
}
// 擴充方法
public static void DeleteCookie(this HttpResponse response, string name, string path = "/", string domain = null)
{
var cookie = new HttpCookie(name) { Expires = DateTime.Now.AddDays(-1), Path = path };
if (domain != null) cookie.Domain = domain;
response.Cookies.Add(cookie);
}
// 使用
Response.DeleteCookie("AuthToken");
Cookie 完整屬性解析
| 屬性 | 說明 | 預設值 | 安全建議 |
| Name | Cookie 名稱 (唯一鍵) | - | 避免敏感詞彙、使用前綴區分功能 |
| Value | 字串值 (需 URL 編碼) | - | 不存放敏感資料、長度 < 4KB |
| Expires | 過期時間 (DateTime.MinValue = Session Cookie) | Session | 設定合理過期、避免永久 |
| Domain | 適用網域 | 當前網域 | 子域共享設 `.example.com`、謹慎使用 |
| Path | 適用路徑 | / | 限制最小路徑範圍 |
| HttpOnly | 禁止 JS 存取 | false | 必須 true (除非需 JS 讀取) |
| Secure | 僅 HTTPS 傳輸 | false | 生產環境必須 true |
| SameSite | 跨站請求攜帶策略 | Lax (新版) | Strict/Lax/None (跨域需 None+Secure) |
SameSite 屬性深度解析 (ASP.NET 4.7.2 / .NET Core 2.1+)
| 模式 | 行為 | 適用場景 | 注意事項 |
| Strict | 完全禁止跨站攜帶 (含連結點擊) | 高安全性功能 (轉帳、刪除) | 用戶從外部連結進入需重新登入 |
| Lax | 允許安全跨站 (GET 連結、表單) | 大多數認證 Cookie (推薦預設) | 平衡安全與體驗 |
| None | 允許所有跨站攜帶 | 跨域 API、嵌入式 iframe | 必須搭配 Secure=true |
// 設定 SameSite (需 .NET 4.7.2+ 或使用反射相容舊版)
cookie.SameSite = SameSiteMode.Lax;
// 舊版專案相容寫法 (透過反射)
typeof(HttpCookie).GetProperty("SameSite")?.SetValue(cookie, SameSiteMode.Lax);
實務場景範例
1. 記住登入狀態 (表單認證票據)
// 登入成功後
var ticket = new FormsAuthenticationTicket(1, username, DateTime.Now,
DateTime.Now.AddDays(rememberMe ? 30 : 1), rememberMe, userData);
string encTicket = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encTicket)
{
HttpOnly = true,
Secure = Request.IsSecureConnection,
SameSite = SameSiteMode.Lax,
Path = FormsAuthentication.FormsCookiePath
};
Response.Cookies.Add(cookie);
2. 購物車商品 ID 清單 (客戶端暫存)
// 加入商品
public void AddToCart(int productId)
{
var cookie = Request.Cookies["CartItems"] ?? new HttpCookie("CartItems");
var ids = cookie.Value.Split(',', StringSplitOptions.RemoveEmptyEntries).Select(int.Parse).ToList();
if (!ids.Contains(productId)) ids.Add(productId);
cookie.Value = string.Join(",", ids);
cookie.Expires = DateTime.Now.AddDays(7);
Response.Cookies.Add(cookie);
}
// 讀取購物車
public List GetCartItems()
{
var cookie = Request.Cookies["CartItems"];
return cookie?.Value.Split(',', StringSplitOptions.RemoveEmptyEntries)
.Select(int.Parse).ToList() ?? new List();
}
3. 多語言/地區偏好設定
// 設定語言
Response.Cookies.Add(new HttpCookie("Culture", "zh-TW")
{
Expires = DateTime.Now.AddYears(1),
HttpOnly = true,
SameSite = SameSiteMode.Lax
});
// 全域套用 (Global.asax)
protected void Application_BeginRequest()
{
string culture = Request.Cookies["Culture"]?.Value;
if (!string.IsNullOrEmpty(culture))
{
Thread.CurrentThread.CurrentUICulture = new CultureInfo(culture);
Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(culture);
}
}
安全性最佳實踐清單
- 敏感資料禁存 Cookie:密碼、Token、個資、信用卡號 → 用 Session/Server-side Storage
- HttpOnly = true:防 XSS 竊取 Session ID、認證 Token
- Secure = true:生產環境強制 HTTPS,防中間人攻擊
- SameSite = Lax/Strict:防 CSRF,現代瀏覽器預設 Lax
- 加密 Cookie 值:`MachineKey.Protect/Unprotect` 或 AES 加密
- 設定合理過期:避免 Session Cookie 敏感操作、持久 Cookie 定期輪換
- 限制 Domain/Path:最小權限原則
- 定期審計 Cookie:清理過期、無用、過大 Cookie
現代替代方案對比
| 方案 | 儲存位置 | 容量 | 自動攜帶 | 適用場景 |
| Cookie | 用戶端 | 4KB | 是 (每請求) | 身份識別、輕量偏好、追蹤 |
| Session | 伺服器端 | 無上限 (記憶體) | 透過 Cookie 傳 SessionID | 用戶狀態、購物車、權限暫存 |
| LocalStorage | 用戶端 (JS) | 5-10MB | 否 (需 JS 手動) | 大量客戶端資料、離線快取 |
| SessionStorage | 用戶端 (JS) | 5-10MB | 否 (分頁隔離) | 單一會話暫存資料 |
| IndexedDB | 用戶端 (JS) | 數百 MB | 否 | 結構化離線資料、PWA |
| JWT (Authorization Header) | 用戶端 (記憶體) | ~8KB | 否 (JS 手動附加) | 無狀態 API 認證、微服務 |
編碼處理提醒
// 寫入時編碼
cookie.Value = HttpUtility.UrlEncode("包含中文&特殊字元=值");
// 讀取時解碼
string value = HttpUtility.UrlDecode(cookie.Value);
// 或使用 Base64 避免編碼問題
cookie.Value = Convert.ToBase64String(Encoding.UTF8.GetBytes(originalString));
string decoded = Encoding.UTF8.GetString(Convert.FromBase64String(cookie.Value));
Cookie 雖屬經典技術,但在身份認證、個人化偏好、輕量狀態傳遞仍扮演關鍵角色。掌握 `HttpCookie` 完整屬性、安全性屬性 (HttpOnly/Secure/SameSite)、編碼處理與現代替代方案選擇,能構建安全、相容、可維護的用戶端狀態管理方案。