2013年6月25日

CORS and JSONP

第一次遇到cross origin resource sharing問題是在2010年的時候,我配合amazon affiliate program做了一個很陽春廣告聯播系統,我提供一組js,嵌入這個js的網站可以展示我選的商品,當時就是用JSONP做的,而最後其實也只放在我的一個網站上而已,那時我深信我挑選的商品CPA獲利會比google的廣告CPC還要好,不過後來證明我大大的錯了,一個商品都沒有賣出,自製的廣告聯播系統的開發也就沒有再開發了。


CORS

Cross origin resource sharing,CORS 主要是為了可以取得 cross-domain 的資源,阻止我們用javascript 直接取的 cross-domain 遠端資源的主因是 same origin policy,CORS 提供一種方法可以讓 server 決定是否允許這種 cross-origin 的 request ,這比較麻煩,但是比沒有 same origin policy 來的安全。

CORS 的實作方式比較麻煩,主因是一些特立獨行的瀏覽器的緣故,現行最新的瀏覽器都提供了 XMLHttpRequest 物件,但是IE8自創了一組 XDomainRequest 物件逼你使用,一般檢測方式是看 XMLHttpRequest 創造出來的物件實例是否含有 withCredentials 屬性,沒有的話就要撤退到XDomainRequest,要注意兩者 callback function 返回的參數不同,要另外處理。

再來就是更有古早味的瀏覽器Opera10, < IE 8, < FireFox 3.5,沒有這些功能,必須要用flash來hack,要是遇到沒裝flash的話,基本上就gg。所以 CORS 對於老瀏覽器的支援並不好。另外在server端也要配合,處理這樣的 request,就是我們以前會看到的 crossdomain.xml ,放在domain 根目錄,e.g mydomain.com/crossdomain.xml。

crossdomain.xml 要設定

<?xml version="1.0"?> <!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd"> <cross-domain-policy> <allow-access-from domain="*" /> <allow-http-request-headers-from domain="*" headers="*" /> </cross-domain-policy> 

並且需要修改request 回應的 header


Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Credentials: true Access-Control-Allow-Origin: http://domain-of-the-resource.com Access-Control-Allow-Headers: Content-Type, *

要注意的是Access Control Allow Origin不能使用 wildcard ,IE說不行


JSONP

JSON-P 支援所有瀏覽器,他是利用呼叫遠端的 javascript function 來達到 cross-domain ,這個 function  在回傳時候會被執行,我們可以藉此來或的包覆在其中的資料,要注意的是被包覆的資料不一定要是 JSON 格式,有時候我們不會使用 JSON ,而是返回字串,因為原生JSON 支援是在 IE8+, Firefox 3.1+, Safari 4+, Chrome 3+, Opera 10.5+ 才有的。

server 端的回應處理很簡單,用收到的 callback function name 消毒後把原本的回應包起來就可以了,這裡要小心回應的XSS攻擊,因為 callback function name 可能是任何字串,也可能是一段程式。

由於引用了一段 resource server 的程式碼,resource server 基本上可以對網站做任何事情,使用JSONP 必須要完全信任 resource server 的安全性。

另外要注意的是,JSONP只支援GET request,而且也要小心CSRF,登入的使用者在造訪惡意頁面的時候會被獲取和操作在 resource server 上的資料,由於使用者造訪的網頁不是 resource server 所產生的,所以並沒有一個簡單的方法來防止CSRF,因此第一,不要放敏感的資料,敏感資料使用一般方式傳遞,第二,通常的配合方法是你給這些 partner 網站一組public/private key pair,他們在 request 的時候把 public key 和加密過的訊息傳給你驗證,並且整個流程透過 ssl 來使用。