模糊测试(Fuzz testing)是一种通过向应用程序输入大量随机或半随机数据来发现漏洞和缺陷的技术。它不仅是安全测试的利器,也是提升代码质量的秘密武器。

模糊测试简介

模糊测试能发现的缺陷

模糊测试特别适用于处理复杂或不可信输入的场景,例如媒体解码器、Web 服务器、移动应用或加密工具等。

它能捕获的主要问题包括:

特别是 OWASP Top 10 中的常见漏洞(如 SQL 注入、XSS、反序列化问题等),模糊测试都能有效检测。

Google Chrome 的模糊测试实践

Google 是模糊测试的忠实粉丝。通过模糊测试,Google 发现了超过 25,000 个 Chrome 缺陷,并将其纳入自动化安全检测流程。Google 还建议开源社区在 Java 软件供应链中采用模糊测试,以增强对内存问题和可利用漏洞的防御能力。

Java 环境下的模糊测试实践

在 Java 环境中,推荐使用 CI Fuzz,一个开源且易于上手的 Java 异常测试工具。它可以无缝集成到 JUnit 测试框架中。

cifuzz init
cifuzz create my_fuzz_test
cifuzz run my_fuzz_test
<dependency>
  <groupId>com.code-intelligence</groupId>
  <artifactId>jazzer-junit</artifactId>
  <version>0.13.0</version>
  <scope>test</scope>
</dependency>
<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-engine</artifactId>
  <version>5.9.0</version>
  <scope>test</scope>
</dependency>

Java 库的模糊测试示例

假设我们有一个目标类 ExploreMe,它的代码如下:

package com.example;

public class ExploreMe {
  // 模拟一个方法,接收三个参数并进行逻辑判断
  public static void exploreMe(int a, int b, String c) {
    // 检查输入是否满足特定条件
    if (a >= 20000 && b >= 2000000 && b - a < 100000 && c.startsWith("@")) {
      String className = c.substring(1); // 提取类名
      try {
        Class.forName(className); // 尝试加载类
      } catch (ClassNotFoundException ignored) {
        // 忽略类未找到异常
      }
    }
  }
}

模糊测试用例如下:

import com.code-intelligence.jazzer.api.FuzzedDataProvider;
import com.code-intelligence.jazzer.junit.FuzzTest;

public class FuzzTestCase {
  @FuzzTest
  void myFuzzTest(FuzzedDataProvider data) {
    // 从模糊数据提供器中消费一个随机整数
    int a = data.consumeInt();
    // 消费另一个随机整数
    int b = data.consumeInt();
    // 消费剩余数据作为字符串
    String c = data.consumeRemainingAsString();
    // 调用目标方法进行测试
    ExploreMe.exploreMe(a, b, c);
  }
}

当某个输入导致程序崩溃时,该输入会被加入种子集合,后续用于进一步测试。这种机制可以帮助开发者快速定位问题。

总结

模糊测试是一种强大的测试方法,能够发现崩溃、稳定性、逻辑和安全缺陷。对于 Java 开发者来说,结合 CI Fuzz 和 JUnit,可以快速集成模糊测试,提升代码在面对异常输入时的鲁棒性和安全性。

当某个输入导致程序崩溃时,该输入会被加入种子集合,后续用于进一步测试。这种机制可以帮助开发者快速定位问题。

public class ExploreMe {
  public static void exploreMe(int a, int b, String c) {
    if (a >= 20000 && b >= 2000000 && b - a < 100000 && c.startsWith("@")) {
      String className = c.substring(1);
      try {
        Class.forName(className);
      } catch (ClassNotFoundException ignored) {}
    }
  }
}

模拟模糊测试用例:

import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;

public class FuzzTestCase {
  @FuzzTest
  void myFuzzTest(FuzzedDataProvider data) {
    int a = data.consumeInt();
    int b = data.consumeInt();
    String c = data.consumeRemainingAsString();
    ExploreMe.exploreMe(a, b, c);
  }
}

当某输入导致 crash,该输入将被加入种子集合,后续用于进一步测试  。


FunTester 原创精华


↙↙↙阅读原文可查看相关链接,并与作者交流