通過一篇文章由淺入深的理解JSONP并拓展
簡單描述
JSONP 是 JSON with padding(填充式 JSON 或參數(shù)式 JSON)的簡寫。
JSONP實(shí)現(xiàn)跨域請(qǐng)求的原理簡單的說,就是動(dòng)態(tài)創(chuàng)建<script>
標(biāo)簽,然后利用<script>
的src 不受同源策略約束來跨域獲取數(shù)據(jù)。
JSONP 由兩部分組成:回調(diào)函數(shù)和數(shù)據(jù)?;卣{(diào)函數(shù)是當(dāng)響應(yīng)到來時(shí)應(yīng)該在頁面中調(diào)用的函數(shù)?;卣{(diào)函數(shù)的名字一般是在請(qǐng)求中指定的。而數(shù)據(jù)就是傳入回調(diào)函數(shù)中的 JSON 數(shù)據(jù)。
動(dòng)態(tài)創(chuàng)建<script>
標(biāo)簽,設(shè)置其src,回調(diào)函數(shù)在src中設(shè)置:
var script = document.createElement("script"); script.src = "https://api.douban.com/v2/book/search?q=javascript&count=1&callback=handleResponse"; document.body.insertBefore(script, document.body.firstChild);
在頁面中,返回的JSON作為response參數(shù)傳入回調(diào)函數(shù)中,我們通過回調(diào)函數(shù)來來操作數(shù)據(jù)。
function handleResponse(response){ // 對(duì)response數(shù)據(jù)進(jìn)行操作代碼 }
上面是簡單直接的對(duì)JSONP 的描述,可能有些人不是很懂,我們下面一步一步分析
層層深入
先通過一個(gè)簡單的實(shí)例簡單的理解一下同源策略的作用
首先我們?cè)诒镜貙懭?/p>
1.html
<!DOCTYPE html> <html> <head> <title>GoJSONP</title> </head> <body> <script type="text/javascript"> function jsonhandle(data){ alert("age:" + data.age + "name:" + data.name); } </script> <script type="text/javascript" src="jquery-3.3.1.min.js"> </script> <script type="text/javascript"> $(document).ready(function(){ $.ajax({ type : "get", url : "http://check.k0rz3n.com/test.php?id=1", dataType: "jsonp",//指定我們的請(qǐng)求是一個(gè) jsonp 的請(qǐng)求 success : function(data) {//success 指定的是默認(rèn)的回調(diào)函數(shù) jsonhandle(data); } }); }); </script> </body> </html>
我服務(wù)器上的test.php會(huì)返回json 格式的數(shù)據(jù)給客戶端
test1.php
<?php header('Content-Type:application/json; charset=utf-8'); $data = array('age'=>19,'name'=>'jianshu'); exit(json_encode($data)); ?>
如果正常訪問的話,那么我們的瀏覽器應(yīng)該會(huì)彈出對(duì)話框,結(jié)果我們會(huì)得到這樣的結(jié)果
此處輸入圖片的描述
可以看到,瀏覽器發(fā)現(xiàn)這是一個(gè)跨域的請(qǐng)求,但是他在服務(wù)器的返回頭中缺沒有發(fā)現(xiàn)
Access-Control-Allow-Origin 值允許 http://localhost 的訪問,于是就攔截了。
也就是說,雖然瀏覽器受到了同源策略的限制,不允許實(shí)現(xiàn)跨域訪問,但是由于在開發(fā)過程中前后端的交互過程中不可避免地會(huì)涉及到跨域的請(qǐng)求(設(shè)計(jì)同源策略的人想必也發(fā)現(xiàn)了這個(gè)問題),于是設(shè)計(jì)者給我們留了一個(gè)后門,就是只要服務(wù)器響應(yīng)頭中返回允許這個(gè)源的選項(xiàng),那么跨域請(qǐng)求就會(huì)成功。(這里糾正一個(gè)誤區(qū),不要認(rèn)為瀏覽器默認(rèn)支持同源策略就意味著不同源的請(qǐng)求就不能發(fā)出去,其實(shí)還是能發(fā)出去的,只是要看響應(yīng)頭而已。)
我們知道在頁面中有幾個(gè)東西是對(duì)同源策略免疫的,那就是 <img> 的src 、<link> 的 href 還有就是<script>的 src , JSONP 就是利用 script 標(biāo)簽的sec 屬性實(shí)現(xiàn)跨區(qū)域請(qǐng)求的
script標(biāo)簽的請(qǐng)求不論是不是同源一律不受同源策略的限制,那我們就找到了解決跨域訪問的方法(似乎這個(gè)方法一開始就存在…..)
我們改變一下代碼,本地直接通過script標(biāo)簽請(qǐng)求服務(wù)器上的js,js 的內(nèi)容就是調(diào)用參數(shù)已經(jīng)傳進(jìn)去的本地的js函數(shù)
2.html
<!DOCTYPE html> <html> <head> <title>GoJSONP</title> </head> <body> <script type="text/javascript"> function jsonhandle(data){ alert("age:" + data.age + "name:" + data.name); } </script> <script type="text/javascript" src="jquery-3.3.1.min.js"> </script> <script type="text/javascript" src="http://check.k0rz3n.com/remote.js"></script> </body> </html>
remote.js
jsonhandle({ "age" : 15, "name": "John", })
注意:
(1)遠(yuǎn)程的js 代碼不需要script標(biāo)簽
(2)這其實(shí)也給了我們一些啟示,就是我們使用 callback 函數(shù)請(qǐng)求的頁面實(shí)際上類型是javascript 的類型,我們可以在這里看一下瀏覽器會(huì)將哪些類型當(dāng)做 javascript 解析:https://mathiasbynens.be/demo/javascript-mime-type
下圖可以看到我們成功利用<script>實(shí)現(xiàn)了跨域的訪問。
此處輸入圖片的描述
那JSONP和這個(gè)有啥關(guān)系,感覺已經(jīng)實(shí)現(xiàn)跨域了還沒有提到一點(diǎn)JSONP,上面說JSONP是基于script標(biāo)簽的,個(gè)人感覺JSONP的優(yōu)勢就是能夠?qū)崿F(xiàn)呢比較方便的函數(shù)選擇,傳一個(gè)參數(shù)就行了,不用像直接調(diào)用那樣必須要換js文件。
真相浮現(xiàn)
先用下面的代碼模擬jsonp的調(diào)用過程方便大家更好的理解jsonp的運(yùn)行過程
3.html
<!DOCTYPE html> <html> <head> <title>GoJSONP</title> </head> <body> <script type="text/javascript"> function jsonhandle(data){ alert("age:" + data.age + "name:" + data.name); } </script> <script type="text/javascript" src="jquery-3.3.1.min.js"> </script> <script type="text/javascript"> $(document).ready(function(){ var url = "http://check.k0rz3n.com/test1.php?id=1&callback=jsonhandle"; var obj = $('<script><\/script>'); obj.attr("src",url); $("body").append(obj); }); </script> </body> </html>
test1.php
<?php $data = array( 'age' => 20, 'name' => 'dada', ); $callback = $_GET['callback']; echo $callback."(".json_encode($data).")"; return;
我們?cè)趕cipt標(biāo)簽里面給出的鏈接是我遠(yuǎn)程服務(wù)器的一個(gè)php的代碼,我給這個(gè)文件傳遞了一個(gè)參數(shù),作為我要調(diào)用的函數(shù)。服務(wù)器接收到這個(gè)參數(shù)以后把它當(dāng)做函數(shù)名,并給這個(gè)函數(shù)傳遞了一個(gè)json的值作為用戶調(diào)用的函數(shù)的參數(shù),最終實(shí)現(xiàn)調(diào)用
下面是調(diào)用成功的截圖
實(shí)際上,jquery 給我們提供了現(xiàn)成的接口,我們可以不用這么麻煩
4.html
<!DOCTYPE html> <html> <head> <title>GoJSONP</title> </head> <body> <script type="text/javascript" src="jquery-3.3.1.min.js"></script> <script type="text/javascript"> function jsonhandle(data){ alert("age:" + data.age + "name:" + data.name); } </script> <script type="text/javascript"> $(document).ready(function(){ $.ajax({ type : "get", url : "http://check.k0rz3n.com/test1.php?id=1", dataType: "jsonp", jsonp:"theFunction", //指定回調(diào)函數(shù)在 URL 中的參數(shù)名(不指定默認(rèn)為 callback) jsonpCallback: "jsonhandle",//指定回調(diào)函數(shù)名稱(如果不指定,服務(wù)器會(huì)隨機(jī)分配一個(gè)jQueryxxx 的名字) success : function(data) { console.info("調(diào)用success"); } }); }); </script> </body> </html>
這時(shí)候的請(qǐng)求的 URL 就像下面這個(gè)樣子:
http://check.k0rz3n.com/test1.php?id=1&theFunction=jsonhandle
服務(wù)器端頁面為:
test2.php
<?php $data = array( 'age' => 20, 'name' => 'dada', ); $callback = $_GET['theFunction']; echo $callback."(".json_encode($data).")"; return;
怎么樣,大概理解了吧,其實(shí)可以用一個(gè)非常形象的例子說明:
幼稚園吃午飯,小明吧貼有自己名字的碗(回調(diào)函數(shù))給了幼稚園阿姨(服務(wù)器),阿姨給小明盛好飯(json參數(shù))以后又把碗還給了小明。
就是這樣的一個(gè)過程。
相關(guān)拓展:JSONP攻擊
1.JSONP 跨域劫持
實(shí)際上就是由于服務(wù)器端對(duì)JSONP 的請(qǐng)求來源的檢查不嚴(yán)格導(dǎo)致的
攻擊者模擬用戶向有漏洞的服務(wù)器發(fā)送JSONP請(qǐng)求,然后就獲取到了用戶的某些信息,再將這些信息發(fā)送到攻擊者可控的服務(wù)器
2.JSONP 跨域劫持token 實(shí)現(xiàn)CSRF
通過 jsonp 發(fā)起請(qǐng)求,得到泄露的 csrf_token 然后,利用這個(gè)token 實(shí)現(xiàn)CSRF 攻擊
3.Referer 頭的繞過
在攻擊過程中可能會(huì)涉及到 referer 頭的繞過
- data:URL
為了逃避他的檢測我們可以選擇不發(fā)送referer這個(gè)頭,那么怎么做呢?這就涉及到 data:URL 頭
為了構(gòu)造一個(gè)不帶HTTP Referer的請(qǐng)求,我們可以濫用data URI方案。因?yàn)槲覀冋谔幚淼拇a包含了引號(hào),雙引號(hào),以及其他一些被阻止的語句,接著使用base64編碼我們的payload(回調(diào)函數(shù)定義以及腳本包含)
data:text/plain;base64our_base64_encoded_code:
以下3個(gè)HTML標(biāo)簽允許我們使用data URI方案:
iframe (在src屬性中) – Internet Explorer下不工作 embed (在src屬性中) – Internet Explorer及Microsoft Edge下不工作 object (在data屬性中) – Internet Explorer及Microsoft Edge下不工作
2.從HTTPS向HTTP發(fā)起請(qǐng)求
如果目標(biāo)網(wǎng)站可以通過HTTP訪問,也可以通過將我們的代碼托管在一個(gè)HTTPS頁面來避免發(fā)送HTTP Referer。如果我們從HTTPS頁面發(fā)起一個(gè)HTTP請(qǐng)求,瀏覽器為了防止信息泄漏是不會(huì)發(fā)送Referer header。以上我們要將惡意代碼托管在一個(gè)啟用了HTTPS的站點(diǎn)。
注意:由于mixed-content安全機(jī)制,在瀏覽器默認(rèn)設(shè)置下是不會(huì)工作的。需要受害者手動(dòng)允許瀏覽器發(fā)出的安全警告。
參考鏈接
- https://blog.csdn.net/u011897301/article/details/52679486
- https://blog.csdn.net/u014607184/article/details/52027879
- http://www.91ri.org/13407.html
- http://www.freebuf.com/articles/web/70025.html
- http://www.freebuf.com/articles/web/126347.html
- https://www.cnblogs.com/chiangchou/p/jsonp.html
總結(jié)
到此這篇關(guān)于JSONP拓展的文章就介紹到這了,更多相關(guān)理解JSONP并拓展內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
asp.net下利用js實(shí)現(xiàn)返回上一頁的實(shí)現(xiàn)方法小集
其實(shí)要實(shí)現(xiàn)這個(gè)功能主要還是要用到j(luò)avascript2009-11-11ES6知識(shí)點(diǎn)整理之模塊化的應(yīng)用詳解
這篇文章主要介紹了ES6知識(shí)點(diǎn)整理之模塊化的應(yīng)用,結(jié)合實(shí)例形式分析了基于node.js環(huán)境下模擬ES6模塊化配置與使用相關(guān)操作技巧,需要的朋友可以參考下2019-04-04ZeroClipboard插件實(shí)現(xiàn)多瀏覽器復(fù)制功能(支持firefox、chrome、ie6)
Zero Clipboard 利用透明的Flash讓其漂浮在復(fù)制按鈕之上,這樣其實(shí)點(diǎn)擊的不是按鈕而是Flash ,這樣將需要的內(nèi)容傳入Flash,再通過Flash的復(fù)制功能把傳入的內(nèi)容復(fù)制到剪貼板2014-08-08script標(biāo)簽中的defer和async使用技巧說明
這篇文章主要介紹了script標(biāo)簽中的defer和async使用技巧,包含他們的下載順序和執(zhí)行順序,以及使用場景需要的朋友可以參考下2023-02-02