jsonp安全性防範,分爲如下幾點: javascript
一、防止callback參數意外截斷js代碼,特殊字符單引號雙引號,換行符均存在風險
二、防止callback參數惡意添加標籤(如script),形成XSS漏洞
三、防止跨域請求濫用,阻止非法站點惡意調用 php
針對第三點,咱們能夠經過來源refer白名單匹配,以及cookieToken機制來限制
而前兩點,傳統的作法分爲如下幾種:
一、純手工過濾特殊字符,引號尖括號等,一旦發現潛在惡意字符則服務端拒絕,返回錯誤。此種方式較爲嚴格,可是隨之而來的問題是失敗率會有所提高,尤爲對於對外開發者。並且JS中惡意字符的變形十分多,此方式須要枚舉全部非法字符,可能存在疏漏。咱們不該該將潛在的惡意字符都一律屏蔽,由於確實有些需求須要傳入並存儲這些字符。
二、對於callback參數做嚴整的格式檢查,或強制約定指定格式。基本能夠完全解決安全問題,但一樣是對調用端不是徹底透明,使用者須要額外去知曉相關限制和約定,可能會形成沒必要要的溝通成本。
三、返回包體添加header頭部,強制指定MIME類型,避免按HTML方式解析,防止XSS漏洞。這彷佛是個很完美的解決方案。可是十分詭異的是,在某些版本的火狐瀏覽器下,直接訪問MIME類型爲JAVASCRIPT的請求時,瀏覽器仍然會按照HTML解析。或許是該瀏覽器設計的缺陷,但它忽略了咱們設置的header。沒法保證全部瀏覽器嚴格按照MIME類型解析。 java
咱們的關注點一直在於如何限制用戶輸入,可是請從另一個層面去考慮該問題,或許就會豁然開朗。 chrome
咱們先了解一下JS自己的特性。
JSONP的本質是構造全局回調函數,以後加載script標籤觸發回調函數。
一般咱們使用函數能夠這麼寫 json
function test(){}test();
而在全局的函數也就默認是window的成員。也能夠顯示書寫爲 跨域
window.test = function(){};window.test();
而JS中對象的成員是可使用字符串索引的方式訪問的,故進一步改造爲 瀏覽器
window['test']=function(){};window['test']();
如今有注意到麼,如此一來咱們已經把函數名已經徹底限制在了字符串上下文,理論上只要作好了防注入工做,callback參數是不可能跳出字符串上下文意外執行代碼的。以PHP爲例,單字符串防止注入甚至能夠直接使用json_encode該字符串實現。 安全
window['alert("123");abc']();
上面的callback參數雖然有注入的風險,能夠因爲callback參數嚴格限制在字符串內部,僅會做爲文本,不會意外執行 cookie
但仍然存在xss漏洞問題,看下面例子 xss
window['<script>alert(123);</script>']();
咱們雖然已經保證了<script>嚴格限制在引號內部,不會形成js注入,可是直接在瀏覽器中輸入該jsonp請求仍會按照HTML解析,產生XSS漏洞,即使設置了header也很難防範。
在進一步,咱們只須要保證瀏覽器內不會明文出現<>標籤,那麼問題即可完全解決。
基本思路是在服務端作一次urlencode。而在output輸出decodeURIComponent(‘#####’)來規避顯示尖括號。Urlencode過的字符串,只可能包含字母數字%,也順便解決了注入的問題
最終附上一段簡短的代碼。根本解決jsonp的安全問題
<?php header('Content-type: text/javascript'); //加上此句能夠消除chrome的警告 $callback = urlencode($_GET['callback']); echo "window[decodeURIComponent('{$callback}')] ({ret:0,msg:’OK’});"
請求1:http://www.test.com/a.php?callback=alert(123);abc
響應1:window[decodeURIComponent('alert(123)%3Babc')]({ret:0,msg:'OK'});
請求2:http://www.test.com/a.php?callback=<script>alert(123);</script>
響應2:window[decodeURIComponent('%3Cscript%3Ealert(123)%3B%3C%2Fscript%3E')]({ret:0,msg:'OK'});
上述幾個例子均可以證實jsonp安全漏洞已被完全規避,即使存在嘗試注入的惡意參數,仍能最大限度保證程序徹底正常工做觸發回調。