5

看看这个简单的 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

但是怎么做?为什么面向用户的服务没有捕捉到这一点?

4

4 回答 4

2

对,我明白了。您正在使用两个不同的解析器。最初我虽然这些都是以不同的方式宽容的。“Postel 定律”对于 UI 非常有用,但对于编程接口却很危险。特别是,毛茸茸的解析非常适合创建漏洞。

再看一遍 RFC4627,\u 符号在 JSON 中是合法的。您的一个解析器似乎已损坏并错误地解释了数据。

通常只解析一次 - 相当多的错误来自多次解析。如果您再次需要 JSON 格式的数据,请使用明显符合规范的格式化程序以及您必须处理的任何错误实现来提取和重新编码。

有趣的是,即使应该非常简单,文本格式也容易受到这种事情的影响。因此,更喜欢简单(不是 ASN.1)二进制格式并使用质量库。


原始答案,我错误地假设您每次都在命令行上使用用户数据启动一个新进程:

这与解析 JSON 无关,与您在命令行中输入的内容无关。将不受信任的数据放在命令行上是个坏主意。这属于“注入”类漏洞,还有 SQL 注入、XSS、LDAP 注入、HTTP 请求/响应拆分等。大多数这些漏洞都可以通过严格的格式化库来解决。命令行无处不在,因此最好完全避免使用它们,或者使用 Base64 编码之类的东西。

OWASP 有一个关于Command Injection的页面。

“Java 编程语言的安全编码指南,版本 4.0”有指南 3-4:避免在命令行中出现任何不可信的数据。

(如果您为每个请求启动一个新的 Java 进程,您也很容易让自己受到 DoS 攻击。)

于 2013-10-30T22:37:54.803 回答
2

作为bad\u0020stuff输入,第一个解析器返回一个对象js,其中js.get("cmd")返回字符串bad\u0020stuff,因此commandAllowed返回true。第二个解析器返回一个返回的对象jsjs.get("cmd")bad stuff转换\u0020为空格)。这样bad stuff命令就被执行了。

第一个解析器不遵循 RFC4627,因为它不解释 unicode 转义。

于 2013-12-08T02:28:54.487 回答
0

在我看来,这很可能是quick-json.

quick-json没有\正确转义:https ://code.google.com/p/quick-json/issues/detail?id=6

因此,我认为它也不能正确地逃避它。所以坚持json-simple哪个更符合规范。

于 2013-11-18T14:49:05.033 回答
0

如果您无法使用正确遵循规范的 JSON 库,那么...

org.apache.commons3.lang.StringEscapeUtils.unescapeJava(cmd).equals("bad stuff")

检查这个 SO 问题How to unescape a Java string literal in Java?

于 2013-11-18T19:45:08.953 回答