Have a look at this simple JSON system composed of a user facing service that talks to a back end:
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"); }
}
The user facing service (using quick-json) prevents the user from making the backend (using json-simple) do bad stuff. To simulate the user, I'm using stdin from Main. In a real scenario, Backend would be on another server, or some old legacy / binary code, so we can't simply change it to accept Java objects. The design is that authorization is done separately from Backend. This could be elaborated by making another class called AdminFacingService that only the admin has access to, or by putting if (userIsAdmin) return true; at the top of UserFacingService.commandAllowed.
As you can see, it works as intended:
$ 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
Sending {"cmd": "bad stuff"} should cause the user facing service to reject the command:
$ 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
That works as well. But then if the user sends {"cmd": "bad\u0020stuff"}, bad stuff happens:
$ 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
But how? Why isn't the user facing service catching this?