使用P3P Header解決跨網域IFrame Session遺失問題
3 |
最近協助處理的問題,某個網頁使用IFrame內嵌了來自另一個網站的ASP.NET網頁,被內嵌的網頁有使用到Session,單獨開啟操作時一切正常;但被內嵌使用時,會出現Session無法儲存的問題。而有趣的是,另外單獨開啟Session網頁一次,再回頭使用被內嵌的版本,居然Session功能就正常了。
我用以下的網頁來模擬情境,httq://172.28.1.1/P3P/Main.aspx以IFrame內嵌了另一個網站httq://127.0.0.1/P3P/UserSession.aspx(其實都是同一台機器的同一個Web Application,但我透過對外IP及127.0.0.1模擬成不同網站)
Main.aspx的內容如下,純粹只是一個包含IFrame的容器網頁,另外再顯示Request.Url以便識別:
<%@ Page Language="C#" %>
<!DOCTYPE html>
<script runat="server">
void Page_Load(object sender, EventArgs e) {
u.Text = Request.Url.AbsoluteUri;
}
</script>
<html>
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:Label ID="u" runat="server"></asp:Label><hr />
<iframe src="<% =Request.Url.AbsoluteUri.Replace(
Request.Url.Host, "127.0.0.1").Replace("Main", "UseSession") %>"
style="width: 400px; height: 100px;"></iframe>
</form>
</body>
</html>
UseSession.aspx提供TextBox可輸入要存入Session的字串,按下Save儲存,按Refresh則可顯示存入的Session內容。
<%@ Page Language="C#" %>
<!DOCTYPE html>
<script runat="server">
protected void Page_Load(object sender, EventArgs e)
{
u.Text = Request.Url.AbsoluteUri;
RefreshSessionDisplay();
}
private void RefreshSessionDisplay()
{
l.Text = Server.HtmlEncode((string)Session["Boo"]);
}
protected void b_Click(object sender, EventArgs e)
{
Session["Boo"] = t.Text;
t.Text = "";
RefreshSessionDisplay();
}
protected void r_Click(object sender, EventArgs e)
{
RefreshSessionDisplay();
}
</script>
<html>
<head runat="server">
<title></title>
</head>
<body>
<form id="form1" runat="server">
<asp:Label ID="u" runat="server" />
<div>
Session: <asp:TextBox ID="t" runat="server"></asp:TextBox>
<asp:Button ID="b" runat="server" Text="Save" onclick="b_Click" />
<asp:Button ID="r" runat="server" Text="Refresh" onclick="r_Click" />
</div>
<hr />
<div>
Session[Boo] = <asp:Label ID="l" runat="server"></asp:Label>
</div>
</form>
</body>
</html>
依經驗研判,這應與第三方網站Cookie被拒有關,而ASP.NET的Session又需要以Cookie為憑(可參考ASP.NET Session大排長龍一文),一旦Cookie被禁,ASP.NET Session也就跟著失效了。以前IE7/8的狀態列有個小眼睛Icon可以顯示第三方Cookie被拒的警示,但到IE9不知怎麼就沒了。山不轉路轉,這裡就用IE9 Dev Tools的新功能Network擷取功能來驗證:
開啟網路擷取功能後,輸入內容,按下Save後,再按一次Refresh發生Session遺失,兩次按鈕動作觸發了兩次POST。
在第一個POST中,Response裡出現儲存ASP.NET_SessionId Cookie的Set-Cookie指令。
但在第二個POST Request中,卻沒有附帶任何Cookie,少了ASP.NET_SessionId Cookie,便無法與先前儲存的Session資料聯結在一起,於是Session資料就遺失了。
但獨立開啟UseSession.aspx時,ASP.NET_SessionId可以被儲存,而連帶瀏覽器在對內嵌於Main.aspx中UseSession.aspx發出Request時,也會附上剛才獨立開啟時保存的Cookie,於是便可維繫與Session資料的連結。
而UseSession.aspx的Cookie之所以被禁,是瀏覽器基於維護隱私權,預設封鎖來自第三方網站的Cookie,更精確一點說,應是瀏覽器會封鎖下列兩種第三方 Cookie:不具有精簡原則 (一段簡短扼要的、可供電腦閱讀的隱私權聲明) 的第三方 Cookie,或是具有精簡原則,說明即使未獲得您的默許,仍會使用可識別個人身份的資訊的第三方 Cookie。(參考來源,該篇翻譯不太好懂,稍後會有較實務面的說明)
要解決瀏覽器封鎖Cookie,有兩種做法,一個是要求使用者調整瀏覽器端的設定,將UseSession.aspx的網站列入信任的網站(Trusted Sites)或近端內部網路(Local Intranet),就可享受較寬鬆的限制。而另一個思考方向,就是設法提供所謂的"精簡原則"(Compact Policy),避開瀏覽器的封鎖行為。
所謂的精簡原則,是P3P規範(隱私權偏好選項平台,Platform for Privacy Preferences)的一部分,P3P規範極其複雜,若要說清楚,天都黑一半了。簡單來說,所謂P3P就是網站向瀏覽器表明自己的隱私權政策,例如: 網站是否會蒐集使用者的個人資訊、打算拿Cookie來做什麼用途... 等等,若表現得夠誠懇善良又正直,瀏覽器便會依規則及使用者偏好做出判斷,決定是否接受第三方網站的Cookie。關於P3P的中文資料不多,我找到一篇不錯的文章,有興趣的人可以參考;另外,MSDN裡也有一系列的文章,提供詳細介紹。
部署P3P的完整程序,應包含建立隱私權保護政策文件(policy.html)、原則檔(policy.xml)、原則參考檔(p3p.xml),其中的細節複雜到要靠額外編輯器工具才能搞定。而且要留意,若是對大眾營運的網站,宣告的隱私權保護政策與實際資料蒐集行為不符,會有吃上官司的風險,不可不慎。
但如果我們只是要解決企業內部的跨網站整合,問題就單純多了。只需要在UseSession.aspx加一行:
protected void Page_Load(object sender, EventArgs e)
{
Response.Headers.Add("P3P", "CP=\"NOI ADM DEV COM NAV OUR\"");
u.Text = Request.Url.AbsoluteUri;
RefreshSessionDisplay();
}
在網頁回傳的Header中加入P3P標籤,這些標籤即先前所說的精簡原則,例如: NOI表示不蒐集可識別資料,ADM表示資料為網站管理用... 等等。(RENJIN的文章有介紹一些標籤,完整標籤清單則可參考這裡,MSDN上則有另一分較簡要的清單) 瀏覽器會依照這些標籤判斷是否接受Cookie,理論上標籤應真實反應網站的資料蒐集行為,所以不會有任何官方建議,而瀏覽器怎麼由標籤判別是否接受Cookie也沒有明確的準則,依我自己測試的結果,加上NOI是最省事的做法,一顆見效;不過網站應該很難真的做到NOI,除非永遠匿名,但我想應用在企業內部引發爭議的機率不高(企業系統以公務為主,員工資料也早在系統中, 不太會因Cookie涉及隱私,更何況很多公司連員工MSN都在監聽了... XD)。但還是再提醒一次,P3P隱私權宣告有可能衍生法律議題,請大家自行依實際狀況因時因地制宜,各人造業各人擔囉~~ XD (PS: 除了由ASP.NET回傳Header,也可透過IIS設定或HTML META標籤方式指定P3P原則)
stackoverflow有篇討論提出幾個P3P法律問題的論點值得參考:
- NOI - "Web Site does not collected identified data." 有登入機制、使用者客製化時可能就違背了。
- STP - "Information is retained to meet the stated purpose. This requires information to be discarded at the earliest time possible. Sites MUST have a retention policy that establishes a destruction time table. The retention policy MUST be included in or linked from the site's human-readable privacy policy." 如果宣稱STP,卻沒有明確的保存政策,應該也算詐騙吧? :P
Comments
# by 91
誠懇善良又正直,這不是在說我嘛 XD
# by u329.tw
這在IE6剛出來時,我就遇到此問題了,當時的解法除了改網頁外,也可從IIS下手,這樣應該會比較方便,我將當時的筆記貼在下方,參考~ ---------------------------------------- 解決IE跨Server安控 方法有二,擇一使用: 1.在接收方Server的入口網頁加入: Response.AddHeader "P3P","CP=CAO PSA OUR" 2.在接收方Server的IIS加入Http標題,標題名稱為[P3P],標題值為[CP=CAO PSA OUR] 問題原因在於IE6之後,實做了P3P的機制(Privacy Preferences Project),對於在主視窗中以frame或sub-window的方式開出的網頁會視為是其他網站的網頁,為安全性考量會拒絕送出Cookie值。所以加上header以告知Browser此網頁不需做P3P的考量,Browser即會將Cookie送出,也就能取得想要的session了。
# by Breeze
感謝前輩們的指導 這篇真的對我幫助"無窮盡"的大 我也是同樣的情況 在別人的網頁中main.aspx 嵌入我自己的搜尋程式search.aspx 但他們在使用時選擇完條件按送出後"時常"會跳回main.aspx就像重新整理了一樣 後來去塞log發現"有時"跳回首頁的原因是連search.aspx 裡的按鈕事件都沒有觸發 難怪會沒有參數生成可提供Redirect 但使用前輩們的方法後 此情況完全改善了....只是還不太了解為什麼會解決了...因為我的search.aspx裡並沒有需要記什麼session 難道沒有session也會影響我的參數傳遞嗎??