0%

JSONP原理及其安全性问题

JSONP原理

JSONP(JSON with Padding)是一种跨域获取数据的方法,因为带src属性的标签如<script>允许跨域(1. 在CSP没有设置的情况下, 2.跨域时是可以带cookie的),因此前端可以设置一个callback请求并生成一个访问后台某个<script>的链接,后台在链接中动态的将数据包裹在先前的callback函数中,作为参数返回给前台,这样就完成了跨域获取数据的效果。

简单示例

后台

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import json
from flask import Flask, request, render_template_string

app = Flask(__name__)
app.secret_key='random_secret_key'

@app.route('/remote.js', methods=['GET'])
def remote():
_id=int(request.args.get("id"))
# fetch data from database...
user=[
dict(username="zhangsan",stat="student"),
dict(username="lisi",stat="working"),
dict(username="wangwu",stat="student"),
]
callback=request.args.get("callback")
json_data=json.dumps(user[_id])
template="{{callback}}({{json_data}})"
return render_template_string(template, callback=callback, json_data=json_data)

if __name__ == '__main__':
app.run(host='127.0.0.1', port=8888, debug=True)

这样,加入我们访问http://127.0.0.1:8888/remote.js?id=1&callback=mycallback,那么会得到如下结果:

1549265659864

前台

那么前端就可以嵌入一个<script>标签,其中src设置为http://127.0.0.1:8888/remote.js?id=1&callback=mycallback,以此获取数据:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<script type="text/javascript">
// 得到查询结果后的回调函数
var mycallback = function(data){
alert('username: ' + data.username + ', status: '+ data.stat);
};
</script>
<!-- 提供jsonp服务的url地址(不管是什么类型的地址,最终生成的返回值都是一段javascript代码) -->
<script src="http://127.0.0.1:8888/remote.js?id=1&callback=mycallback"></script>
</head>
<body>
</body>

可以看到获取数据成功:

1549266866289

同时给出jQuery的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$(document).ready(function(){
$.ajax({
type: "get",
async: false,
url: "http://127.0.0.1:8888/remote.js?id=1",
dataType: "jsonp",
jsonp: "callback",//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(一般默认为:callback)
jsonpCallback:"mycallback",//自定义的jsonp回调函数名称,默认为jQuery自动生成的随机函数名,也可以写"?",jQuery会自动为你处理数据
success: function(json){
alert('username: ' + data.username + ', status: '+ data.stat);
},
error: function(){
alert('fail');
}
});
});

JSONP的安全性问题

JSON劫持导致CSRF

主要表现为后台没有做身份认证,导致任何前台都可以发送JSONP请求,若请求中存在敏感信息则会发生信息泄露(相当于CSRF)。

防御方法

  1. 使用referer过滤,对于空referer不予以放行
  2. 增加一个随机token

可以看到整体的防御方法类似于CSRF的防御方法。

空referer

进行基于referer的防御时,对于空referer的情况应不予以放行,因为攻击者可以使用某些标签(iframe)构造空referer。Payload如下:

1
<iframe src="javascript:'<script>function callback(o){alert(o.username);}</script><script src=http://127.0.0.1:8888/remote.js?id=1&callback=callback></script>'"></iframe>

自定义Callback导致XSS

比如说本文的实例,攻击者可以构造如下链接导致反射型XSS:

1
http://127.0.0.1:8888/remote.js?id=1&callback=<script>alert(/xss/)</script>

防御方法

  • 定义Content-Type: application/json
  • 对XSS字符进行能过滤