In this article
脆弱なJSONObjectシリアライゼーション(CVE-2023-5072)からJava Spring Boot APIを保護する
JSONObjectのシリアライゼーションに関する重大な脆弱性、CVE-2023-5072が最近発見され、Java開発者、特にSpring Bootフレームワークを利用する開発者にとって重大なセキュリティ上の懸念が浮き彫りになりました。
JSONObjectのシリアライゼーション処理の不具合に起因するこのメモリ不足の脆弱性は、Javaアプリケーションをさまざまなセキュリティ脅威にさらす可能性があります。
CVE-2023-5072は、シリアライゼーションおよびデシリアライゼーションの過程でデータ構造の整合性が損なわれる可能性がある、対処の難しい問題です。効率性と堅牢なJavaアプリケーションを容易に開発できることから広く利用されているSpring Bootで構築されたアプリケーションでは、特に重要な問題です。
デジタル環境がますます複雑化するなか、Javaアプリケーションのセキュリティと信頼性を維持するには、このような脆弱性の性質と影響を理解することが不可欠です。
この記事では、シンプルなJava APIを自作し、この問題の概要をわかりやすく説明するとともに、Spring Boot API環境でリスクを軽減するための実践的な手順を紹介します。
JSONObject CVE-2023-5072の影響を受けるSpring Boot Java API
JSONObjectのJSONシリアライゼーション脆弱性を実証するため、Spring BootとMavenを使って小規模なJava APIサーバーを作成します。/api/todosへのPOSTリクエストを処理し、To-Do項目の配列を含むJSONオブジェクトを解析して、その件数をHTTPレスポンスで返します。
それでは、このプロジェクトの構築手順を順を追って見ていきましょう。
ステップ1:プロジェクトをセットアップする
1. 新しいSpring Bootプロジェクトを作成する:
a. Spring Initializrを使ってプロジェクトを生成できます。
b. ビルドツールとしてMavenを選択します。
c. 依存関係に「Spring Web」を追加します。
d. プロジェクトをダウンロードして解凍します。

2. プロジェクトを開く:
a. お好みのIDE(IntelliJ IDEA、Eclipse、VSCodeなど)でプロジェクトを開きます。
b. Spring Bootの依存関係とspring-boot-maven-pluginのビルドエントリーを含むpom.xmlファイルがあることを確認します。
ステップ2:Javaの依存関係を追加する
pom.xmlで依存関係を管理する方法
Javaで広く使われているビルドツールMavenのpom.xmlファイルでは、プロジェクトに必要な依存関係(外部ライブラリやモジュール)を、groupId、artifactId、versionの組み合わせで指定します。これらの要素を組み合わせることで、Mavenリポジトリ内の特定バージョンのライブラリを一意に識別できます。
pom.xmlファイルを編集し、Spring Webの依存関係があることを確認します。JSONObjectライブラリの依存関係を追加しましょう。Spring Bootに含まれていない場合は、次の依存関係を追加してorg.jsonのJSONライブラリを使用できます。
xml
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20230618</version>
</dependency>
各要素の意味を、特にartifactIdに注目して説明します。
groupId:通常、ライブラリを作成した組織やグループのドメイン名を逆順にしたものです。このサンプルアプリケーションでは、org.jsonがgroupIdであり、このライブラリがjson.orgという組織によって保守されていることを示します。
artifactId:実際のライブラリまたはモジュールの名前です。一般に、ライブラリについて話すときに使う名称です。この例のartifactIdはjsonで、追加するライブラリの名前が単に「json」であることを示します。artifactIdには、ライブラリの主な機能や識別名を反映させるのが一般的です。
version:使用するライブラリの特定のバージョンを指定します。この例のバージョン20230618は、jsonライブラリを一意に識別するバージョン番号です。新しいバージョンではプロジェクトと互換性のない変更が加わる可能性があるため、ビルドの一貫性を保つうえでバージョン管理は重要です。
つまり、pom.xmlファイルの依存関係宣言は、Mavenに対して、org.jsonグループに属するjsonライブラリのバージョン20230618をダウンロードしてプロジェクトに含めるよう指示します。ビルドツールのMavenが中央リポジトリからライブラリを自動的に取得し、プロジェクトで使用できるようにします。
ステップ3:Java APIコントローラーを作成する
ここでは、独自のJavaコードをプロジェクトに追加して、新しいAPIを作成します。この新しいAPIエンドポイントは、Javaアプリケーションが提供する/api/todos URIへのPOST HTTPリクエストを定義します。
新しいコントローラークラスを作成する:
プロジェクトで
src/main/java/<your-group-id>/<your-artifact-id>/に移動し、controller/という新しいディレクトリを作成します。Spring Initializrのデフォルト設定を使用した場合、このパスはsrc/main/java/com/example/demo/controller/DemoApplication.javaになります。TodoController.javaという名前の新しいJavaクラスファイルを作成します。
コントローラーのコードを追加する:
クラスに
@RestControllerアノテーションを付けます。POSTリクエストを処理するメソッドを作成します。
以下は、TodoController.javaクラスファイルの全体例です。
java
import org.json.JSONObject;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TodoController {
@PostMapping("/api/todos")
public String handleTodo(@RequestBody String todoData) {
JSONObject jsonObject = new JSONObject(todoData);
int count = jsonObject.getJSONArray("todos").length();
return "Count: " + count;
}
}ステップ4:サーバーを実行する
Java Spring Boot APIサーバーを起動しましょう。主な方法は次の2つです。
通常はF5キーを押すなどして、IDEからアプリケーションを実行します。
ターミナルでプロジェクトのルートディレクトリに移動し、
mvn spring-boot:runコマンドを実行します。この方法を使うには、開発環境にMavenがインストールされている必要があります。
これでJava APIエンドポイントをテストし、POSTリクエストを送信する準備が整いました。PostmanやcURLなどのツールを使って、http://localhost:8080/api/todosにPOSTリクエストを送信します。本文には、たとえば{"todos": ["Task 1", "Task 2"]}のようなJSON文字列を指定します。
POSTリクエストの送信に使えるcURLコマンドの例を示します。
sh
curl -X POST http://localhost:8080/api/todos \
-H "Content-Type: application/json" \
-d '{"todos": ["Task 1", "Task 2", "Task 3"]}'HTTPレスポンスは次のようになります。
sh
< HTTP/1.1 200
< Content-Type: text/plain;charset=UTF-8
< Content-Length: 8
< Date: Mon, 27 Nov 2023 10:23:51 GMT
<
* Connection #0 to host localhost left intact
Count: 3SnykでJava Spring Bootプロジェクトをテストする
Java開発では、アプリケーションのセキュリティと整合性を確保することは、機能性を実現することと同じくらい重要です。そのための有効なツールの1つがSnykです。プロジェクトの依存関係にある脆弱性の特定と修正を専門とするセキュリティプラットフォームです。
Java開発者は、既存のワークフローにより適した方法を好むことがよくあります。MavenプラグインとしてSnykを使い、Javaアプリケーションをテストする方法を見ていきましょう。
SnykをMavenプロジェクトに統合する
Snykは、pom.xmlに追加できるMavenプラグインを提供しています。このプラグインを使うと、Mavenのビルドプロセスの一環としてSnykのテストを実行できます。
Snyk Mavenプラグインをpom.xmlに追加します。
xml
<plugin>
<groupId>io.snyk</groupId>
<artifactId>snyk-maven-plugin</artifactId>
<version>2.2.0</version>
</plugin>プラグインのセットアップ後、Snykアカウントで認証する必要があります。まだアカウントをお持ちでない場合は、Snykに登録してください。Snykのウェブサイトで無料でアカウントを作成できます。
次に、Snykアカウントの設定からSnyk APIトークンを生成し、使用しているマシンまたはCI/CD環境で環境変数SNYK_TOKENとして設定します。MavenのSnykテストを実行するCLIで利用できるようにするには、次のコマンドを実行します。
sh
export SNYK_TOKEN=<the Snyk API token goes here>Snykプラグインに明示的な設定を指定し、apiTokenとコマンドライン引数をより詳細に設定することもできます。このSnyk Mavenプラグインの設定は、<plugin>宣言に追加します。
xml
<configuration>
<apiToken>${env.SNYK_TOKEN}</apiToken>
<args>
<arg>--all-projects</arg>
</args>
</configuration>続いて、Snykのテストを実行します。
sh
mvn snyk:testおっと、追加したJSONObjectの依存関係に脆弱性があることをSnykが検出したようです。ビルドプロセスで出力された、次のMavenのSnykテストログを確認してください。
sh
$ mvn snyk:test
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.example:demo >--------------------------
[INFO] Building demo 0.0.1-SNAPSHOT
[INFO] from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- snyk:2.2.0:test (default-cli) @ demo ---
[INFO] Snyk Executable Path: /Users/lirantal/Library/Application Support/Snyk/snyk-macos
[INFO] Snyk CLI Version: 1.1254.0
[INFO]
[INFO] Testing /Users/lirantal/projects/repos/spring-boot-java-jsonobject-vulnerability...
[INFO]
[INFO] Tested 37 dependencies for known issues, found 1 issue, 1 vulnerable path.
[INFO]
[INFO]
[INFO] Issues to fix by upgrading:
[INFO]
[INFO] Upgrade org.json:json@20230618 to org.json:json@20231013 to fix
[INFO] ✗ Allocation of Resources Without Limits or Throttling [High Severity][https://security.snyk.io/vuln/SNYK-JAVA-ORGJSON-5962464] in org.json:json@20230618
[INFO] introduced by org.json:json@20230618
[INFO]
[INFO]
[INFO]
[INFO] Organization: snyk-demo-567
[INFO] Package manager: maven
[INFO] Target file: pom.xml
[INFO] Project name: com.example:demo
[INFO] Open source: no
[INFO] Project path: /Users/lirantal/projects/repos/spring-boot-java-jsonobject-vulnerability
[INFO] Licenses: enabled
[INFO]
[INFO]
[ERROR]
[ERROR] You have reached your monthly limit of 401 private tests for your snyk-demo-567 org.
[ERROR] To learn more about our plans and increase your tests limit visit https://snyk.io/plans.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 12.507 s
[INFO] Finished at: 2023-11-27T14:42:01+02:00
[INFO] ------------------------------------------------------------------------Java ChampionでSnykのセキュリティアドボケイトでもあるBrian Vermeerが、MavenプラグインでSnykを使用する方法について詳しいブログ記事を執筆しています。
JSONObjectのメモリ不足バグを実証する
SnykはJSONパーサーパッケージでサービス拒否型の脆弱性を検出し、このセキュリティ問題について詳しく調べられるSnykの脆弱性データベースを案内しています。
そこでは、Java APIサーバーが脆弱である理由を理解するために利用できる概念実証が紹介されています。脆弱性を引き起こす入力をJSONパーサーに与え、何が起きるか確認してみましょう。
TodoController.javaのコードに戻り、不正な形式のJSONペイロードを生成する次のprivateメソッドを追加します。
java
private static String makeNested(int depth) {
if (depth == 0) {
return "{\"a\":1}";
}
return "{\"a\":1;\t\0" + makeNested(depth - 1) + ":1}";
}次に、handleTodo() APIルートのメソッドを更新し、その文字列をJSONObjectコンストラクターの入力として使います。
java
String vulnerablePayload = makeNested(30);
JSONObject jsonData = new JSONObject(vulnerablePayload);mvn spring-boot:runでJava APIサーバーを再起動し、cURLのPOST HTTPリクエストをもう一度送信してみましょう。レスポンスが返るまでサーバーがしばらく停止するのがわかります。この停止中にメモリリークが発生し、JVMが使用するメモリが増え続け、最終的にJavaアプリケーションがクラッシュします。
Snykはすでに、このセキュリティ問題の修正方法を示しています。Mavenのsnyk:testビルドをもう一度実行すると、バージョン20230618から20231013にアップグレードして、このサービス拒否の脆弱性を修正するようSnykが案内していることを確認できます。
sh
[INFO] Tested 37 dependencies for known issues, found 1 issue, 1 vulnerable path.
[INFO]
[INFO]
[INFO] Issues to fix by upgrading:
[INFO]
[INFO] Upgrade org.json:json@20230618 to org.json:json@20231013 to fix
[INFO] ✗ Allocation of Resources Without Limits or Throttling [High Severity][https://security.snyk.io/vuln/SNYK-JAVA-ORGJSON-5962464] in org.json:json@20230618
[INFO] introduced by org.json:json@20230618この問題を修正するには、pom.xmlファイルを編集し、org.jsonの依存関係のバージョンを20231013に更新してください。
安全なJavaアプリケーションの開発、ビルド、リリースに役立つリソース
JSONObjectの依存関係を含むJava Spring Boot APIの構築に使用したソースコード全体は、次のリポジトリを参照してください:https://github.com/snyk-snippets/spring-boot-java-jsonobject-vulnerability
Javaコードの品質に高い基準を設け、高品質な製品のリリースに取り組んでいる方は、Javaアプリケーションのセキュリティについてさらに学ぶため、次のリソースをご覧ください。
Brian VermeerとJim ManicoによるJavaセキュリティのベストプラクティス10選チートシート
Brian VermeerによるJavaの依存関係を管理するためのベストプラクティスの記事
セキュアなJava開発に向けたSnykの利用を始める
SnykのMavenプラグインはオープンソースで、こちらから利用できます:https://github.com/snyk/snyk-mvn-plugin
Javaアプリケーションのセキュリティについて学ぶには、Snyk Learnをご覧ください