淺談 ASP.NET Cookie 安全設定
3 |
Cookie 安全性近年來常成為網站弱點掃瞄或滲透測試的重點,其中常被糾舉彈劾的點是:
- Cookie 應限定加密通訊(SSL/TLS)時傳遞,降低被竊聽外流的風險。
- Cookie 應限定伺服器讀取,禁止 JavaScript 透過 document.cookie 存取以防盜用。
HTTP 協定已有 Cookie 安全的相關規範,使用 Chrome F12 開發工具檢視 Cookie 便可看到 HttpOnly、Secure、SameSite 等旗標:
HttpOnly 表示此 Cookie 限伺服器讀取設定,document.cookie 無法存取;Secure 限定使用 HTTPS 連線才准許在 Request 附上 Cookie;SameSite 則跟隱私與第三方 Cookie 有關,共有三種安全等級 Strict、Lax、None。若想進一步了解細節,推薦最近看到蠻完整易懂的教學文 - Cookie 與 document.cookie by Shubo。
回到主題,在 ASP.NET 裡,我們該怎麼強化 Cookie 安全性?答案挺簡單,改 web.config 加入以下設定即可:
<system.web>
<httpCookies requireSSL="true" httpOnlyCookies="true" />
<!-- ...略... -->
</system.web>
不深入了解原理,遇到疑難雜症就只能狂喊見鬼,我習慣實驗演練過一次心裡才踏實。
先不加 httpCookies 設定,我寫了以下程式測試 Server 端設定 Cookie、讀取 Cookie 及用 JavaScript document.cookie 三種動作:(順便練習完全不用 jQuery,純 JavaScript 搞定)
<%@ Page Language="C#" %>
<script runat="server">
void Page_Load(object sender, EventArgs e)
{
if (Page.IsPostBack)
{
var m = Request["m"];
var msg = string.Empty;
var cookieName = "Z";
if (m == "chk")
{
msg = "[Svr] Cookie = " + Request.Cookies[cookieName]?.Value;
}
else if (m == "set")
{
var cookieValue = DateTime.Now.ToString("ssfff");
var cookie = new HttpCookie(cookieName, cookieValue);
Response.Cookies.Add(cookie);
msg = "[Svr] Cookie set to " + cookieValue;
}
Response.Write("<script>parent.showMsg('" + msg + "');<" + "/script>");
Response.End();
}
}
</script>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Cookie Test</title>
<style>
button {
display: block;
width: 200px;
margin-bottom: 3px;
}
#msgList {
width: 300px;
background-color: #eee;
padding: 6px;
margin-top: 12px;
min-height: 30px;
}
</style>
</head>
<body>
<form id="form1" runat="server" target="postResult">
<div>
<input name="m" id="m" type="hidden"/>
<button onclick="document.getElementById('m').value = 'chk'">
Check Cookie (Server)
</button>
<button onclick="document.getElementById('m').value = 'set'">
Set Cookie (Server)
</button>
<button onclick="showMsg('document.cookie => ' + document.cookie);" type="button">
Check Cookie (Client)
</button>
</div>
</form>
<div id="msgList">
</div>
<script>
function showMsg(msg) {
var msgList = document.getElementById('msgList');
msgList.innerHTML +=
"<div>" + msg + "</div>"
}
</script>
<iframe name="postResult" style="display:none"></iframe>
</body>
</html>
實測結果如下,跟我們傳統認知的一樣,一開始沒有 Cookie,呼叫伺服器端設定 Cookie,之後伺服器端可以讀到 Cookie,JavaScript 端也可以讀到:
接著我在 web.config 加上 <httpCookies httpOnlyCookies="true" />
,再測一次。一開始伺服器端與 JavaScript 都能讀到上次設定的 Cookie 值,但重設過一次 Cookie 後,伺服器仍能讀到新值,JavaScript 讀不到,原因是重新寫入的 Cookie 被加上了 HttpOnly 旗標:
再來,在 web.config 改成 <httpCookies requireSSL="true" />
,再開一個瀏覽器連 HTTPS,測試結果如下:
圖中上方的瀏覽器連 http、下方連 https,兩個網頁的 Cookie 資料是共用的,數字是我測試的順序,由於步驟有點多,這裡解釋一下發生了什麼事:
- 雖然已開啟 requireSSL="true",但瀏覽器既有的 Cookie 的 Secure 旗標未設定,故走 http 仍能讀到
- 未設 Secure 的 Cookie 送 https 也是沒問題的
- 用 http 重設 Cookie
- 用 http 讀取仍是舊值而非剛才的設定的新值! 因為現在無法透過非 https 寫入 Cookie,故 Cookie 值仍維持原樣
- 因為 Cookie 未被覆寫,https 讀到的也是舊值
- 用 https 重設 Cookie
- https 可以讀到新值
- http 已讀不到值,原因是覆寫過新 Cookie Secure = true,不允許走 http 傳到到伺服器端
由以上實驗,相信大家已了解 httpOnlyCookies="true" 與 requireSSL="true" 的影響,並能預期切換後既有 Cookie 的行為,如此應可開始在專案部署 Cookie 強化設定囉。
Tips of how to use web.config httpCookies requireSSL and httpOnlyCookies to enhance cookie security in ASP.NET.
Comments
# by Colo
請問瀏覽器既有的 Cookie 的 Secure 旗標可以設定嗎? 我在web.config內加上requireSSL="true" 後, 客戶的瀏覽器有的用ie 11有的用 chrome, 使用http仍能讀到有的卻不行...
# by Jeffrey
to Colo,你指是的清除 http 留下的 Cookie?除了將 Cookie 更名,我沒想到其他好方法。
# by 小黑
突然想到 google chrome 說即將於 2024 年逐步淘汰第三方 cookie ,這是否說cookie 建議少用,都用替代解決方案,若系統已經使用多年,有賺錢的系統就需要調整維持運作,沒賺錢的系統就趁機協調下架了?