看看这个简单的 JSON 系统,它由一个与后端对话的面向用户的服务组成:
import java.util.*;
import com.json.parsers.*;
import org.json.simple.*;
class Main{static {
String s = new java.util.Scanner(System.in).useDelimiter("\\A").next();
UserFacingService.handleMessage(s);
}}
class UserFacingService {
static void handleMessage(String s) {
JSONParser parser = JsonParserFactory.getInstance().newJsonParser();
Map js = parser.parseJson(s);
if (commandAllowed(js))
Backend.handleMessage(s);
else
System.out.println("unauthorized!");
}
static boolean commandAllowed(Map js) {
if (js.get("cmd").equals("bad stuff"))
return false;
return true;
}
}
class Backend {
static void handleMessage(String s) {
JSONObject js = (JSONObject) JSONValue.parse(s);
String cmd = (String)js.get("cmd");
if (cmd.equals("bad stuff")) {
doBadStuff();
} else if (cmd.equals("ping")) {
doPong();
}
}
static void doBadStuff() { System.out.println("doing bad stuff"); }
static void doPong() { System.out.println("doing pong"); }
}
面向用户的服务(使用quick-json)防止用户让后端(使用json-simple)做坏事。为了模拟用户,我使用来自Main
. 在真实场景中,Backend
可能是在另一台服务器上,或者是一些旧的遗留/二进制代码,所以我们不能简单地将其更改为接受 Java 对象。设计是授权与Backend
. 这可以通过创建另一个名为AdminFacingService
只有管理员才能访问的类来详细说明,或者if (userIsAdmin) return true;
将UserFacingService.commandAllowed
.
如您所见,它按预期工作:
$ CP=.:json-simple-1.1.1.jar:quick-json-1.0.2.3.jar
$ javac -cp $CP A.java && echo '{"cmd": "ping"}' | java -cp $CP Main
doing pong
Exception in thread "main" java.lang.NoSuchMethodError: main
发送{"cmd": "bad stuff"}
应该导致面向服务的用户拒绝命令:
$ CP=.:json-simple-1.1.1.jar:quick-json-1.0.2.3.jar
$ javac -cp $CP A.java && echo '{"cmd": "bad stuff"}' | java -cp $CP Main
unauthorized!
Exception in thread "main" java.lang.NoSuchMethodError: main
这也有效。但是如果用户发送{"cmd": "bad\u0020stuff"}
,就会发生不好的事情:
$ CP=.:json-simple-1.1.1.jar:quick-json-1.0.2.3.jar
$ javac -cp $CP A.java && echo '{"cmd": "bad\u0020stuff"}' | java -cp $CP Main
doing bad stuff
Exception in thread "main" java.lang.NoSuchMethodError: main
但是怎么做?为什么面向用户的服务没有捕捉到这一点?