前兩天幫同事處理一個 js 跨域問題,使用 jsonp 跨域提交用戶名密碼請求,實現自動登錄第三方網站,即 SSO(single-sign-on) 單點登錄,一處登錄處處登錄。在 Chrome 下沒問題,IE 卻不行。查看 HTTP 的幾個來回,發現登錄請求是成功的,問題出在第三方網站返回的 cookie (session id) IE 並沒有接受,下一次發送請求時根本沒有帶上 cookie,說明之前的 Set-Cookie 指令沒有效果,所以怎麼也登錄不了。查了一下,有人使用 iframe 內嵌網頁的形式,也遇到了 IE 下不能設置 cookie 的情況。

如果在“Internet選項”中把“隱私”級別設置為低,或者把第三方域名列入“可信站點”就沒問題了。但是我們不可能讓每個用戶去更改 IE 設定吧?這是一個很常遇到的場景,肯定有別的解決辦法。

瀏覽器的第三方 cookie 限制

所謂第三方 cookie,就是說你訪問網頁 A,卻接收到域名 B 的 cookie 設定指令。這可能是由於網頁 A 請求或鏈接了 B 的網頁,比如上面提到的 iframe 以及 jsonp。

我查到了各個瀏覽器對於跨域的處理規則,可以看到第三方 cookie ,IE 在默認設置中是做了限制的。

不同瀏覽器的第三方 cookie 規則
IE FireFox Chrome Safari Opera
限制第三方coookie

要解決這個問題,有 2 種方法,一個就是上面說到的調整 IE 設置,將第三方域名加入到可信網站列表中;另一個方法,就是 P3P 了。

P3P?

P3P 全稱 Platform for Privacy Preferences,隱私設定平台規範。這個規範極其複雜,若要講清楚,天都黑了一半。簡言之,就是網站向瀏覽器聲明自己的隱私政策,比如網站是否搜集訪問者的個人信息,設置 cookie 的用途等等。瀏覽器會依據設置,決定在第三方請求的條件下是否接受網站的 cookie。

完整地部署 P3P 包括設立隱私政策文件(policy.html)、原則檔(policy.xml)、參考檔(p3p.xml),有興趣詳細了解的可以參考 MSDN 中關於部署 P3P 的文章。

這搞得太複雜了,我只是想在公司內部做各個管理系統的單點登錄而已。好在還是有比較簡單的方法的,就是發送 P3P 相關的 HTTP header。

ASP.NET:

HttpContext.Current.Response.AddHeader("p3p", "CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"");

PHP:

header('P3P:CP="IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT"');

JSP:

response.setHeader("P3P","CP='IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT'");

好吧,這些 IDC DSP 什麼的是啥意思啊?

這些標籤就是 P3P 所規定的了,例如 NOI 表示不搜集可識別用戶的資料,ADM 表示信息搜集會用於網站管理……查看完整清單中文簡要清單

瀏覽器會根據這些標籤決定是否接受 cookie,根據測試結果,加上 NOI 最省事,一個就夠了。不過網站一般很難做到 NOI,除非永遠匿名,“登錄”功能可能就違背了NOI。理論上講,標籤應該真實地反映網站的信息搜集行為,若聲明的隱私政策與實際行為不符,是會要負法律責任的。Stackoverflow 有篇討論提出了法律相關議題,可以參考。

除了傳送 P3P http header,還可以通過 HTML meta 標籤,或者 設定 IIS 服務器 來聲明 P3P。

參考鏈接:
http://blog.darkthread.net/post-2011-10-27-p3p-header-and-iframe-session.aspx