2010-10-08

2010-10-08 Facebook Javascript SDK in IE8 problem

自從Facebook今年初發布 Graph API 並開始提供新的 SDK 以後,Facebook的相關APP開發規範就一直在進行修正調整,同時原本開發者費了不少時間才熟悉的FBML APP將會被淘汰掉,

Facebook Markup Language (FBML) - Facebook Developers:裡面有提到:
Note: We do not recommend FBML for new developers. If you aren't already using FBML, you should instead implement your application within an iframe, using the JavaScript SDK and social plugins for client-side integration with Facebook services . The one exception: if you absolutely must create an application that appears as a tab on a Facebook Page, you will need to use FBML for now; tabs do not currently support iframes directly. We will be transitioning tabs to iframes later this year -- please see the developer roadmap for more details.

而在 2010-08-20 的Facebook Developer Blog Facebook Platform Roadmap Update (2010-8-20) 中更是明確指出:
By the end of this year, we will no longer allow new FBML applications to be created, so all new canvas applications and Page tabs will have to be based on IFrames and our JavaScript SDK.

既然Facebook都這樣說了,那就乖乖的來重新熟悉 iframe + JavaScript SDK 的開發模式,結果開始以後才發現 JavaScript SDK 說明文件 跟之前的developer wiki (新API公佈後沒多久就被拿掉了)相比資料少的可憐;資料少也就算了, 還有不少莫名其妙的問題。
尤其是在IE8的環境下更是討厭,
以下就把這段時間遇到的問題跟解決方式整理一下作個紀錄:

1. FB.Event.subscribe的auth.login事件在IE8底下會有無窮迴圈的問題

用IE8開啟 測試APP 1 http://apps.facebook.com/fbuiwithiexssfilter/test1/

這個簡單的測試APP 在Firefox / Chrome 這些非IE的瀏覽器下都運作正常,但是在IE8底下


FB.Event.subscribe('auth.login', function() {
window.location.reload();
});

就會出現無窮迴圈不斷地reload


關於這問題在 Facebook Platform Developer Forum 中有人提出兩種解法http://forum.developers.facebook.net/viewtopic.php?id=60411

第一種是在HTML中加上 讓IE進入相容模式
(此解法在FB.ui 的 stream.publish 發布塗鴉牆內含中文訊息時會有被IE8的XSS filter給阻擋的問題,這部份後面再說明,而且我測試無效)
第二種是在http header中加上 P3P CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"

in Apache2 , add
header add P3P 'CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"'
in .htaccess file (must enable apache mod_headers)
= or =
in php , add
header('P3P: CP="NOI ADM DEV PSAi COM NAV OUR OTRo STP IND DEM"');

用工具看http header可以確認是否有正確加入 P3P的http表頭


經過這樣處裡後原先 FB.Event.subscribe 的 auth.login事件在IE8底下就正常了

可用IE8開啟 測試APP 2 http://apps.facebook.com/fbuiwithiexssfilter/test2/ 進行測試
跟前面測試APP 1一樣的code在這就不會有無窮迴圈的問題了。




2.FB.ui stream.publish 發布塗鴉牆內含中文訊息時會被IE8的XSS (Cross Site Scripting) filter判定為cross-site attack而阻擋的問題
現在Facebook對於APP塗鴉牆發布做了很嚴苛的限制,之前大家都在用的自動發布的方式很容易被判定為違規而被禁用塗鴉牆發布功能(已經聽到不少APP被砍或是塗鴉牆發布被禁用的案例,尤其是8月開始Facebook的自動偵測系統大發威,砍掉不少app),所以要發布塗鴉牆都建議採用popup出預覽待使用者確定後發出才比較安全,在新的Facebook Javascript SDK中此種方法用的是FB.ui ,但是也有不少朋友在開發測試時遭遇到FB.ui開啟的內容被IE8的XSS filter給擋掉的問題,造成在IE8底下沒法正常發送塗鴉牆。

這問題也卡了我很久,全用英文就沒這問題,只要有非英文字元就有可能觸發,而且中文字越多機率越高,就算運氣好沒有被IE8 XSS filter擋掉,但是也會非常的慢(CPU Loading暴升且把IE8卡住),怎麼交叉測試都沒用,有時多一字/少一字結果就完全不同,而把增減的那字獨立拿出測試又沒問題,實在是找不到規則可尋。

IE8開啟 測試APP 3 http://apps.facebook.com/fbuiwithiexssfilter/test3/

其中的 pubish with english content (dialog) | pubish with english content (popup) 這兩個按鈕都可以正常打開發布塗鴉牆的預覽頁面(純英文內容),
但是發布有中文訊息的塗鴉牆就容易被IE8的XSS filter給阻擋掉
pubish with chinese content (dialog) 內嵌dialog視窗模式

pubish with chinese content (popup) 外跳popup視窗模式


雖然說可以把IE8的XSS filter給關掉就不會有這被卡的問題了

但是以APP開發者的立場來說不可能要求使用者把IE8的這個重要安全功能給disable掉,所以還是得要找出有效解才行。

在經過多方測試比對後發現是我犯了一個差不多先生等級的低級錯誤,就是HTML最前方的!DOCTYPE定義區段沒寫才造成的。這在非IE8瀏覽器下基本都不會有影響的,但是在IE8底下就會造成這問題!

IE8你也太太太要求標準了吧 ~~

用IE8開啟 測試APP 4 http://apps.facebook.com/fbuiwithiexssfilter/test4/

在這個測試中在HTML最前面補上了 <!DOCTYPE html>
同樣的中文內容在測試APP3中會被擋掉的在這就都可以正常了



在這裡又要順帶一提前面提到的IE8相容性檢視了,若是在IE8針對要用FB.ui發布塗鴉牆的頁面打開相容性檢視的話,中文內容依舊有可能會觸發XSS filter




所以用 Facebook Javascript SDK 的 FB.ui 發布中文內容塗鴉牆,為了配合廣大的IE8 User
務必要遵循標準在HTML最前面加上 <!DOCTYPE html> ,同時User得要避免使用IE8 的相容性檢視(Compatibility View) 功能才行。

還有一件事要提一下,在九月底十月初Facebook已經把APP發布的塗鴉牆訊息在動態消息(News Feed)中的呈現方式改掉了, 在 Facebook Developer Blog 2010-09-22的
Making Games on Facebook Better裡面寫到
Application stories will only be shown to those who are already engaging with the application.
也就是說在User的動態消息(News Feed)中現在只看得到自己曾經安裝過的APP的訊息,像以前那樣想要靠發布塗鴉牆來散布宣傳吸引其他使用者進來的時代已經結束了,這對不玩遊戲的User是個好消息,但對APP開發者來說是個噩耗!之前改掉通知時就一堆APP慘兮兮了,後來大家改用塗鴉牆來散布消息,現在又改這就讓APP開發者/經營者幹到沒力啊。

寫到這還是要靠么一下

在Facebook Platform Developer Forum 裡面這位開發者說的 這段話也是我想說的:

even after three years of this cr@p is still amazes me how on earth Facebook manage to push things like this to the live site, do they have no testing procedures at all?

另外這篇 The Facebook API: A Case Study in Not Caring About Developers
POOR DOCUMENTATION
POOR TESTABILITY
NO RESPONSE TO SERIOUS ISSUES
CONSTANTLY CHANGING API

也道出了Facebook Application Developers的心聲

很多問題文件資料都沒寫,得要Developer自己想辦法去論壇大海撈針才行,問題是有幾個Developer有那麼多美國時間整天泡在論壇上? 很機車咧!!

最後再打個廣告
反非死不可粉絲頁 歡迎跟我一樣被Facebook搞到起度爛的開發者一起來交流交流 ~

10 則留言:

sinkcup 提到...

感谢博主的facebook feed IE8 XSS研究和demo。
我用的是oldjavascript旧版API,就没这么幸运了,FB.Connect.streamPublish不能发过长的feed,好像是iframe URL过长就会被IE8识别为XSS。
例如IE8识别下面的URL为XSS,而旧版的facebook feed就是通过URL传递数据的……新版用flash,就OK了。
http://www.fanfou.com/?locale=zh_CN&method=stream.publish&channel=http%3A%2F%2Flocalhost%2Ffacebook%2Fxd_receiver.html&extern=1&message=message%20Check%20out%20this%20cute%20pic.&attachment=%7b%22name%22%3a%22%e7%8d%b2%e5%be%97%e4%ba%86%e7%a8%80%e6%9c%89%e6%9d%90%e6%96%99%e6%ab%bb%e6%a1%83%ef%bc%81%22%2c%22href%22%3a%22http%3a%2f%2fapps.facebook.com%2fhello_world_%2f%22%2c%22caption%22%3a%22%7b*actor*%7d+%e7%8d%b2%e5%be%97%e6%9d%90%e6%96%99%22%2c%22description%22%3a%22%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%e5%ad%97%22%2c%22media%22%3a%5b%7b%22type%22%3a%22image%22%2c%22src%22%3a%22http%3a%2f%2fstatic.apollo.happyelements.com%2fapollo%2fimg%2fname36.jpg%22%2c%22href%22%3a%22http%3a%2f%2fapps.facebook.com%2fhello_world_%2f%22%7d%5d%7d&action_links=%5B%7B%22text%22%3A%22%E8%AF%95%E8%AF%95%E6%B8%B8%E6%88%8F%22%2C%22href%22%3A%22http%3A%2F%2Fapps.facebook.com%2Fhello_world_%2F%22%7D%5D&user_message_prompt=Your%20mind%20is%20mine.&display=iframe&session_key=2.oZim2kjPqeI_X_6Z_ia3kA__.3600.1286611200-100001551563922&next=http%3A%2F%2Flocalhost%2Ffacebook%2Fxd_receiver.html%23fname%3D_parent%26%257B%2522t%2522%253

Wisely Song 提到...

Hi zhou,
新版的看來似乎不是用flash, 我把flash block插件打開還是能發。
還是用URL的方式只是他沒有透過xd_receiver而是直接傳到facebook的uiserver.php去了,

在test4 的popup測試中可以看到發送的url如下
https://www.facebook.com/connect/uiserver.php?access_token=XXXXXXX&api_key=xxxxxxxx&attachment={%22name%22:%22\u4e2d\u6587%22,%22caption%22:%22\u9019\u662f\u4e2d\u6587%22,%22description%22:%22\u9019\u662f\u4e2d\u6587%20\u4e00\u4e8c\u4e09\u56db\u4e94\u516d\u4e03\u516b\u4e5d\u5341\uff0c\u58f9\u8cb3\u53c3\u8086\u4f0d\u9678\u67d2\u634c\u7396\u96f6%20%22}&display=popup&locale=en_US&method=stream.publish&next=http://static.ak.fbcdn.net/connect/xd_proxy.php%23cb%3Df3d9d7e38c%26origin%3Dhttp%253A%252F%252Ffacebookvoteone.alwaysdata.net%252Ff9254b02%26relation%3Dopener%26transport%3Dpostmessage%26frame%3Df3579d9c7%26result%3D%2522xxRESULTTOKENxx%2522&sdk=joey

我另外複製修改了一個test5 http://apps.facebook.com/fbuiwithiexssfilter/test5/ 來測試長內容,
結果發現過長的內容時新版JS-SDK就會自動改成用POST的方式來傳遞參數了,在 popup 測試的url列就只SHOW出 https://www.facebook.com/connect/uiserver.php 而不像test4 那樣用GET方式把所有參數帶在後面了

Wisely Song 提到...

另外,用舊版的Javascript API的話可以試試
http://forum.developers.facebook.net/viewtopic.php?pid=257087 所說的
在 http header裡面加上
X-XSS-Protection: 0
因為url呼叫的標的網址是自己的;

我是過這方法在新版就無效了,因為新版就直接導去 https://www.facebook.com/connect/uiserver.php 了,除非FB把 X-XSS-Protection 加在他們 https://www.facebook.com/connect/ 的http表頭中

做最好的自己 提到...

試驗了一下,用舊版的API,也是無法設置http header的。
調用FB.Connect.streamPublish後,JS在頁面中彈出一個層,dialog_content中便是一個iframe,其src為http://www.facebook.com/connect/uiserver.php?..........。IE 8 彈出XSS的提示,便是因為這個鏈接後面的參數,如果想disable xss,還是要在facebook的uiserver.php這個頁面中增加X-XSS-Protection: 0 的http header。
所以,增加X-XSS-Protection: 0 的方案對舊版API也不適用。

Wisely Song 提到...

Hi. 做最好的自己.
看來這問題目前現階段還真是無解,也不知道是該怪Facebook還是怪IE8的XSS filter

我看到有人在facebook Bug Tracker上開了一個disable IE8 XSS protection的request
http://bugs.developers.facebook.net/show_bug.cgi?id=12912
一起去VOTE吧

sinkcup 提到...

博主,你好。我刚看了test5測試,的确是POST,直接打开了https://www.facebook.com/connect/uiserver.php,而没有GET参数。
但是我在test4里把汉字加长,仍然是GET,uiserver.php?asdf=qwer。
有点纳闷,test4和test5为何一个是post,一个是get。

Wisely Song 提到...

Hi Zhou,
我大致看了下
http://connect.facebook.net/en_US/all.js
(沒有很仔細去trace,不過應該八九不離十)
a.params = FB.JSON.flatten(a.params);
var c = FB.QS.encode(a.params);
if ((a.url + c).length > 2000) {
a.post = true;
} else if (c) a.url += '?' + c;
return a;

看起來是計算url列的字數,length超過兩千就改用post了
所以在TEST5裡面英文內容是get ,而中文內容因為encode以後超過了2000就變post了

Unknown 提到...

這篇很有幫助,謝謝

小煎 提到...

hi, 小歪
你可以用
http://www.blog.highub.com/javascript/javascript-advanced/convert-chinese-characters-to-unicode/

把 chinese string convert to unicode char 的方式
經過我的測試應該是

message 不OK
其他 attachment 的中文都 work
提供給你參考, 謝謝

ps: 你的文章相當受益, 請繼續加油. thank u.


小煎

么ㄦ 提到...

Test 4 在ie9 仍然會被擋掉,而且也是只有中文會被擋..誰叫我們的客戶是微軟```