0x00 前言
關於JSONP網上有很多文章了,我也是在拜讀了別人的文章的基礎上來寫寫自己的看法,這樣可以加深自己印象,鞏固一下學習效果。我們需要做的就是站在巨人的肩膀上眺望遠方。
0x01 起
在Web前端開發中有一種安全機制,Javascript腳本只能訪問與它同域的內容,這就是同源策略。
這里就需要先說明一個問題:如果確定腳本的域?
html頁面中試用腳本有兩種方式:內聯和引用。
上圖中滑紅框部分,src為引用方式,直接在script中寫代碼是內聯方式。
如果html的頁面url為:http://a.huangjacky.com/a.html,那么內聯方式腳本的域是a.huangjacky.com,這應該比較容易理解。
引用方式有兩種情況,引用的js文件就是A站點、引用的js是其他站點,但是不過是內聯還是引用方式的腳本代碼它執行都是在當前html下,因此它的域是和html相同的。
圖中例子用的是AJAX網絡請求,其實Javascript訪問內容還可以DOM操作,當iframe之間的域不相同時,也無法通過Javascript去操作其他iframe中的DOM元素。
上面這個還比較基礎,老鳥別噴。
現在開始我們的例子:
- 有兩個站點a.huangjacky.com, b.huangjacky.com
- A站點的a.html要去請求B站點的b.php
a.html的代碼如上圖,b.php的代碼如下:
1 <?php 2 echo"from b site";
我們點擊a.html中按鈕看一下會是什么樣的效果?
提示說的很清楚,A站的Javascript的XMLHttpRequest對象無法加載B站,因為沒有Access-Control-Allow-Origin頭,那么接下來我們就來看看這個HTTP頭有什么用?
在跨域HTTP請求中,會有一個HTTP頭叫Origin,上圖中也可以看出來,響應中HTTP頭中的Access-Control-Allow-Origin包含Origin域,那么就滿足跨域,瀏覽器就不會報錯了,接下來我們修改PHP代碼:
1 <?php 2 header("Access-Control-Allow-Origin: http://a.huangjacky.com"); 3 echo"from b site"
代碼中就增加一行header設置,看看運行效果:
運行正常了,AJAX也獲得到了B站數據了,但是如果有個C站也要獲取B站數據呢?我們分別嘗試設置Access-Control-Allow-Orgin為http://huangjacky.com和http://*.huangjacky.com。都出現了通源策略訪問錯誤的提示,我這里就不截圖了,我肯定是試了的。
現在問題出來了,我就是有這樣的需求,多個子域都要訪問B站來獲取數據,怎么辦?
其實這里可以設置Access-Control-Allow-Orgin: * ,就可以了。
但是這樣安全性又是一個問題了,所有的網站都可以訪問來訪問數據了,而Access-Control-Allow-Orgin頭要么是*,要么指定子域名。其實這里我們可以在PHP中做一些邏輯判斷,比如判斷Origin頭是否huangjacky.com的請求,那么就設置Access-Control-Allow-Orgin為*,不就可以了么?代碼如下:
1 <?php 2 $origin = $_SERVER['HTTP_ORIGIN']; 3 if(preg_match('/^http:\/\/\w+\.huangjacky\.com/i',$origin)) 4 header("Access-Control-Allow-Origin: *"); 5 echo"from b site";
具體其他的參數可以參考附錄中的詳細文檔咯。
0x02 承
JSONP也是Web開發中針對跨域提出來一種方法,它不是新技術,反而是一些老技術的整合,這樣在瀏覽器的兼容性上面做得不錯。前面我們說到過script標簽的src是可以跨域引用js,把js加載到自己的域中來執行。因此我們可以在引用js的同時提交一些參數,這樣B站根據參數做出相應的操作后,將操作的結果寫入到js文件中,並返回給A站點,這樣A站點就得到了B站點的數據了。
第一框中function是A站在加載完B站數據后執行的函數。第二框是一個DOM中添加一個script標簽。因此我們現在有必要看看B站后台是怎么實現的:
重點是要輸出要確定Content-Type,然后就是拼湊javascript代碼了。
是吧很簡單的,在jQuery在$.ajax函數中將參數dataType設置成jsonp的話,就會按照這種原理來實現跨域,和XMLHttpRequest沒有半毛錢的關系了。
0x03 轉
從JSONP的原理來看的話,script標簽只能GET方式。還有就是后台程序需要對callback參數進行有效性過濾,不然惡意用戶可以插入攻擊代碼了。一般使用正則:
^[a-z0-9_]+$
來判斷用戶的回調函數名是否合法。
而JSONP中經常還會遇到一個問題就是JSON劫持,這是CSRF中的一種,常用的防御方式都是部署TOKEN。
0x04 附錄
- https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS
- http://www.ibm.com/developerworks/cn/web/wa-aj-jsonp1/
- http://www.nowamagic.net/librarys/veda/detail/224