Cookie 是 HTTP 無狀態協定中維持狀態的核心機制。現代 ASP.NET Core 提供強型別 IResponseCookies/IRequestCookies 介面與 CookieOptions 完整控制屬性;舊版 ASP.NET 使用 HttpCookie 與 HttpResponse.Cookies。本文系統整理兩種框架的 Cookie 操作、安全屬性設定、SameSite 政策、GDPR 合規與常見陷阱。
| 功能 | ASP.NET Core | ASP.NET (Framework) |
|---|---|---|
| 寫入 Cookie | Response.Cookies.Append(key, value, options) | Response.Cookies.Add(new HttpCookie(key, value)) |
| 讀取 Cookie | Request.Cookies[key] 或 TryGetValue | Request.Cookies[key]?.Value |
| 刪除 Cookie | Response.Cookies.Delete(key, options) | Response.Cookies.Add(new HttpCookie(key) { Expires = DateTime.Now.AddDays(-1) }) |
| 選項類別 | CookieOptions | HttpCookie 屬性 |
// Controller / Minimal API / Razor Pages
public IActionResult Login(string username)
{
// 簡單寫入 (Session Cookie,瀏覽器關閉即失效)
Response.Cookies.Append("username", username);
return Content("已寫入 Cookie");
}
public IActionResult Read()
{
if (Request.Cookies.TryGetValue("username", out string value))
return Content($"歡迎回來, {value}");
return Content("找不到 Cookie");
}
var options = new CookieOptions
{
// 必要安全屬性
HttpOnly = true, // 禁止 JavaScript 讀取 (防 XSS 竊取)
Secure = true, // 僅 HTTPS 傳輸 (生產環境必開)
SameSite = SameSiteMode.Strict, // 防 CSRF:Strict/Lax/None
// 生命週期
Expires = DateTimeOffset.UtcNow.AddDays(30), // 持久化 Cookie
// MaxAge = TimeSpan.FromDays(30), // 替代 Expires (相對時間)
// 作用域
Path = "/", // 整站可用
Domain = ".example.com", // 子域名共享 (可選)
// 進階
IsEssential = true // GDPR:標記為必要 Cookie,同意條幅不阻擋
};
Response.Cookies.Append("auth_token", jwtToken, options);
// 必須指定相同 Path、Domain、Secure、SameSite
Response.Cookies.Delete("auth_token", new CookieOptions
{
Path = "/",
Domain = ".example.com",
Secure = true,
SameSite = SameSiteMode.Strict
});
// 主域名 example.com 設定
options.Domain = ".example.com"; // 開頭加點,包含所有子域名
// 或精確指定
options.Domain = "app.example.com"; // 僅此子域名
// 子域名 api.example.com 讀取時自動攜帶
// 建立帶子鍵的 Cookie (舊版特有功能)
var cookie = new HttpCookie("user_prefs")
{
HttpOnly = true,
Secure = true, // 需 IIS 設定 RequireSSL
Expires = DateTime.Now.AddDays(30),
Domain = ".example.com",
Path = "/"
};
cookie.Values["theme"] = "dark";
cookie.Values["lang"] = "zh-TW";
cookie.Values["timezone"] = "Asia/Taipei";
Response.Cookies.Add(cookie);
// 讀取子鍵
if (Request.Cookies["user_prefs"] != null)
{
string theme = Request.Cookies["user_prefs"]["theme"];
string lang = Request.Cookies["user_prefs"]["lang"];
}
// 刪除
var delCookie = new HttpCookie("user_prefs") { Expires = DateTime.Now.AddDays(-1) };
Response.Cookies.Add(delCookie);
document.cookie) 存取。localhost + dotnet dev-certs https --trust)。Secure=true,並設定 ForwardedHeaders Middleware。| 值 | 行為 | 適用場景 |
|---|---|---|
Strict | 完全禁止跨站請求攜帶 (含導航連結) | 高安全性 API、管理後台、金融交易 |
Lax (預設) | 允許安全跨站導航 (GET 連結),阻擋 POST/iframe | 大多數網站預設平衡選擇 |
None | 允許所有跨站請求 (需同時設 Secure=true) | 跨域 API、嵌入式 Widget、SSO 跨域登入 |
現代瀏覽器預設行為:Chrome 80+ / Edge / Firefox 預設 Lax。明確設定避免相依預設值變更。
IsEssential = true,無需同意即可寫入。// Startup.cs / Program.cs
services.Configure(options =>
{
options.CheckConsentNeeded = context => true; // 所有非必要 Cookie 需同意
options.MinimumSameSitePolicy = SameSiteMode.Lax;
options.Secure = CookieSecurePolicy.Always;
});
app.UseCookiePolicy(); // 必須在 UseAuthentication 之前
// 寫入非必要 Cookie 時檢查同意
if (Context.RequestServices.GetRequiredService()
.CanTrack)
{
Response.Cookies.Append("ga_id", "GA1.2.xxx", new CookieOptions { IsEssential = false });
}
<div id="cookie-banner" class="fixed bottom-0 w-full bg-gray-900 text-white p-4">
本網站使用 Cookie 改善體驗。<button id="accept-all" class="ml-4 btn btn-primary">接受全部</button>
<button id="accept-essential" class="ml-2 btn btn-outline">僅必要</button>
</div>
<script>
document.getElementById('accept-all').onclick = () => {
document.cookie = "cookie_consent=all; path=/; max-age=31536000; SameSite=Lax";
document.getElementById('cookie-banner').remove();
};
document.getElementById('accept-essential').onclick = () => {
document.cookie = "cookie_consent=essential; path=/; max-age=31536000; SameSite=Lax";
document.getElementById('cookie-banner').remove();
};
</script>
Delete 內部是設定 Expires = 過去時間。**Path、Domain、Secure、SameSite 必須與建立時完全一致**,瀏覽器才會匹配並移除。Domain=.example.com (開頭有點)、SameSite=None; Secure=true (跨域需求)、父域名未設 __Host- 前綴 (禁止 Domain 屬性)。fetch(url, { credentials: 'include' }) 或 axios.defaults.withCredentials = true;後端 CORS 必須 AllowCredentials = true 且 AllowOrigins 不能為 `*` (需具體域名)。CookieAuthenticationOptions 使用 DataProtection 自動加密/簽章。自訂 Cookie 可用 IDataProtector 手動保護。Cookie 操作看似簡單,實則涉及 安全屬性 (HttpOnly/Secure/SameSite)、作用域、生命週期、法律合規、跨域傳輸 多個維度。現代 ASP.NET Core 專案建議:
CookieOptions 強型別設定,避免字串拼接錯誤。HttpOnly=true, Secure=true, SameSite=Strict/Lax。SameSite=None, Secure=true, Domain=.parent.com。CookiePolicy + 前端同意條幅滿足 GDPR/個資法。