1

【茶包射手日記】Cookie 名稱能不能用 $ 起首?

 2 years ago
source link: https://blog.darkthread.net/blog/cookie-startswith-dollar-sign/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

【茶包射手日記】Cookie 名稱能不能用 $ 起首?-黑暗執行緒

見識不夠,踩到一個 ASP.NET Cookie 命名地雷。

在某個 ASP.NET 專案用 Response.Cookies.Add() 新增名為 "$myCookie" 的 HttpCookie,之後用 Request.Cookies["$myCookie"] 卻讀不到值,試著把名稱改成 "_myCookie" 就正常。心想,有可能 $myCookie 不是合法的 Cookie 名稱,於是我寫了小程式想重現問題:

<%@Page Language="C#"%>
<script runat="server">
const string cookieName = "$myCookie";
string cookieValue;
void Page_Load(object sender, EventArgs e)
{
	cookieValue = Request.Cookies[cookieName] == null ? 
		string.Empty : Request.Cookies[cookieName].Value;
	if (Request["m"] == "set") 
	{
		Response.Cookies.Add(new HttpCookie(
			cookieName, new Random().Next().ToString()));
		Response.Redirect("default.aspx");
	}
}
</script>
<!DOCTYPE html>
<html>
	<head>
	</head>
	<body>
		<div>
			Cookie["<%=cookieName%>"] = <%= cookieValue %>
		</div>
		<a href="?m=set">Set Cookie</a>
	</body>
</html>

你猜怎麼著,居然成功了。所以 $myCookie 可以當成 Cookie 名稱呀!

Fig1_637951270999007721.png

那... 到底 $ 可不可以當成 Cookie 名稱開頭?

爬文研究後後,得到以下結論。

  1. 依 RFC,$ 為合法的 Cookie 名稱字元 依據 RFC 6265,Cookie 命名遵守 RFC2616 Section 2.2 Token 規範,$ 不在禁用之列:參考
    token          = 1*<any CHAR except CTLs or separators>
    CHAR           = <any US-ASCII character (octets 0 - 127)>
    CTL            = <any US-ASCII control character
                     (octets 0 - 31) and DEL (127)>
    separators     = "(" | ")" | "<" | ">" | "@"
                     | "," | ";" | ":" | "\" | <">
                     | "/" | "[" | "]" | "?" | "="
                     | "{" | "}" | SP | HT
    
  2. MS 文件,Cookie 名稱不能以 $ 起首 參考

    The following characters must not be used inside name: equal sign, semicolon, comma, newline (\n), return (\r), tab (\t), and space character. The dollar sign character ("$") cannot be the first character.

再研究了一下,大概知道是怎麼一回事了。在古早年代,Cookie 規範以 RFC 2109 為準,在 4.2.2 節提到有所謂 Attribute-Value Pair (Version、Domain、Path、Max-Age、Secure、Comment),而 Cookie 名稱不能以 $ 開頭:

NAME=VALUE
      Required.  The name of the state information ("cookie") is NAME,
      and its value is VALUE.  NAMEs that begin with $ are reserved for
      other uses and must not be used by applications.

在 4.4 則提到 $ 起首的 Cookie 名稱為相鄰 Cookie 的屬性值

When it receives a Cookie header, the origin server should treat cookies with NAMEs whose prefix is $ specially, as an attribute for the adjacent cookie.

不過呢,在 2000 年,RFC 2965 取代了 RFC2109,2011 年再被 RFC 6265 取代,當今 Cookie 規範已不再使用 $Path、$Domain 等名稱標註 Cookie 屬性,$ 也不再是 Cookie 名稱字首保留字元。

由此推測,雖然現代瀏覽器已不再使用 $ 起首 Cookie 設定屬性,但 ASP.NET 為向前相容,仍遵循古禮,將 $ 字首 Cookie 視為相鄰 Cookie 的屬性。最早的範例之所以可以成功,是因為只有一個 Cookie 沒有相鄰 Cookie,$myCookie 被視為獨立 Cookie 值。因此,我們只需隨便加個 Cookie 就能重現 $ 起首 Cookie 讀不到的狀況。

	if (Request["m"] == "set") 
	{
		Response.Cookies.Add(new HttpCookie(
			cookieName, 
			new Random().Next().ToString()
		));
		// 隨便再多設一個 Cookie
		Response.Cookies.Add(new HttpCookie(
			"A", "1234"
		));
		Response.Redirect("default.aspx");
	}

成功重現問題! 瀏覽器識別出 $myCookie、A 兩個 Cookie,但 ASP.NET 讀不出 $myCookie。

Fig2_637951270999669574.png

所以,在 2022 年,瀏覽器可以接受名稱以 $ 起首的 Cookie 沒問題,至於伺服器能不能接受,依平台而定。ASP.NET 不支援,在文件也有載明。至於其他平台,來用 PHP 跑看看:

<?php
$cn = '$myCookie';
$m=$_GET['m'] ?? '';
if ($m == 'set') {
	setcookie($cn, rand(0, 100000));
	setcookie('A', '1234');
	header('Location: index.php');
	die();
}
?>
<!DOCTYPE html>
<html>
	<head>
	</head>
	<body>
		<div>
			Cookie["<?php echo $cn ?>"] = <?php echo $_COOKIE[$cn] ?? '' ?>
		</div>
		<a href="?m=set">Set Cookie</a>
	</body>
</html>

實驗證明,PHP 可以接受以 $ 起首的 Cookie 名稱!

Fig3_637951271000102134.png

所有謎團解開,再增長一些知識。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK