Cookie 安全性近年來常成為網站弱點掃瞄或滲透測試的重點,其中常被糾舉彈劾的點是:

  1. Cookie 應限定加密通訊(SSL/TLS)時傳遞,降低被竊聽外流的風險。
  2. 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 資料是共用的,數字是我測試的順序,由於步驟有點多,這裡解釋一下發生了什麼事:

  1. 雖然已開啟 requireSSL="true",但瀏覽器既有的 Cookie 的 Secure 旗標未設定,故走 http 仍能讀到
  2. 未設 Secure 的 Cookie 送 https 也是沒問題的
  3. 用 http 重設 Cookie
  4. 用 http 讀取仍是舊值而非剛才的設定的新值! 因為現在無法透過非 https 寫入 Cookie,故 Cookie 值仍維持原樣
  5. 因為 Cookie 未被覆寫,https 讀到的也是舊值
  6. 用 https 重設 Cookie
  7. https 可以讀到新值
  8. 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

Be the first to post a comment

Post a comment


10 + 8 =