Skip to main content

Java DTOを使ってセキュリティを確保する方法

feature java dto

2022年10月11日

0 分で読めます

Javaのデータ転送オブジェクト(DTO)は、サブシステム間でデータを受け渡すオブジェクトです。データを集約するためのエンタープライズ設計パターンであり、サブシステム間で必要となるシステムコールの回数を減らし、それに伴うオーバーヘッドを軽減することを主な目的としています。

この記事では、最新のJavaアプリケーションでDTOがどのように使われているか、アプリケーションにどのようなメリットをもたらすか、そして意図しないデータ漏えいを防ぐことでJava DTOがセキュリティ向上にどう役立つかを解説します。

POJO、Java Bean、Value Objectとは

名前のとおり、Plain Old Java Object(POJO)は一般的なJavaオブジェクトです。Java言語で定められた制約以外に、特定の制限に縛られることはなく、どのようなクラスでもPOJOになり得ます。再利用性と可読性を高めるために作られます。

public class CoffeePOJO {

   public String name;
   private List<String> ingredients;

   public CoffeePOJO(String name, List<String> ingredients) {
       this.name = name;
       this.ingredients = ingredients;
   }

   void addIngredient(String ingredient) {
       ingredients.add(ingredient);
   }
}

Java Beanは、JavaBean標準に準拠したPOJOです。この標準では、すべてのプロパティをprivateにし、getterメソッドとsetterメソッドを使ってアクセスします。また、引数なしのコンストラクタを用意するなど、いくつかの要件があります。

つまり、Java BeanはすべてPOJOですが、POJOがすべてJava Beanというわけではありません。

public class CoffeeBEAN implements Serializable {

   private String name;
   private List<String> ingredients;

   public CoffeeBEAN() {
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public List<String> getIngredients() {
       return ingredients;
   }

   public void setIngredients(List<String> ingredients) {
       this.ingredients = ingredients;
   }
}

Value Objectは、単純なデータエンティティを表す小さなオブジェクトです。ただし、通常は識別子を持ちません。現在、JavaではValue Objectを利用できませんが、JDKのメンテナーがJEP 401の一環として導入に取り組んでいます。それまでは、代わりにPOJOを作成する必要があります。

Data Transfer Objectの実装

DTOはPOJOとして実装できますし、Java Beanとして実装することもできます。最も重要なのは、たとえばプレゼンテーション層やドメインモデルなど、異なる要素の関心事をDTOによって分離することです。

これを説明するために、小規模なRESTサービスを例に見てみましょう。コーヒーと顧客を扱うコーヒーショップがあるとします。この2つは、システム内で別々のドメインエンティティです。顧客のお気に入りのコーヒーを知りたい場合、FavoriteCoffeDTOで表される集約データを返すAPIを作成します。

プレゼンテーション層のJSON、API、FavoriteCoffeeDTO、Controllerが、ドメイン層のCustomerとCoffeeにつながっていることを示す図。

コードは次のようになります。

​​public class Coffee {

   private Long id;
   private String name;
   private List<String> ingredients;
   private String preparation;

}

public class Customer {

   private Long id;
   private String firstName;
   private String lastName;
   private List<Coffee> coffees;

}

public class FavoriteCoffeeDTO {

   private String name;
   private List<String> coffees;

}

上記の実装では、ドメイン層とプレゼンテーション層を分離しました。これにより、コントローラーで2つのドメインエンティティをDTOにマッピングできるようになります。

この例ではフィールドをprivateにしたため、アクセスにはgetterメソッドとsetterメソッドを作成する必要があります。DTOでは、JavaBean標準に全面的または部分的に従う方法がよく選ばれます。もちろん必須ではないので、要件に合った方法を自由に選べます。ほかにも、すべてのフィールドをpublicにして直接アクセスする方法(次の例を参照)や、全引数コンストラクタとgetterメソッドを使って不変オブジェクトにする方法があります。

public class FavoriteCoffeeDTO {

   public String name;
   public List<String> coffees;

}

String name = favCoffeeDTO.name;

最後に、より新しいJavaバージョンにアップデートしている場合は、DTOにJavaのレコードを使う方法もあります。Javaのレコードはシンプルな不変クラスで、定義しなくても全引数コンストラクタ、アクセサーメソッド、toString()、hashCode()が自動的に提供されます。コードが簡潔になり、読みやすさも向上します。レコードのアクセサーメソッドにはgetやsetの接頭辞がないため、Java Bean仕様に準拠しない点に注意してください。

public record FavoriteCoffeeDTO(String name, List<String> coffees) {}

String name = favoriteCoffeeDTO.name();

優れたDTOとは

DTOの目的は、プロセス間でデータを受け渡すことです。そのため、優れたDTOには、システムの該当部分で必要とされる情報だけが含まれます。先ほどのAPIの例では、顧客の名前とお気に入りのコーヒーの注文内容を返せば十分です。ビジネスロジックなど、ほかの情報を追加する必要はありません。DTOはできるだけシンプルで、小さく、分かりやすく保つのが基本です。

また、DTOを初期化した後は、その状態を変更したり更新したりしないのが望ましいです。そのため、DTOには不変のデータ構造が適しています。DTOは変更されるべきでないデータだけを保持するため、Javaのレコードは特におすすめです。JacksonなどのJSONシリアライゼーションライブラリがJavaのレコードをサポートしている点もメリットです。

DTOのセキュリティに関する考慮事項

このDTOパターンを使うと、ドメインモデルとプレゼンテーション層を分離できることはすでに確認しました。サブシステムやAPIで必要なデータだけを含み、ビジネスロジックを持たないシンプルなDTOは、セキュリティの向上にも役立ちます。

概念実証(PoC)でよく見られるのは、ドメインエンティティの情報をすべて出力してしまうことです。その結果、システム外で不要なデータが利用可能になり、データ漏えいにつながるおそれがあります。

DTOを使わずに、顧客全員を検索する機能をAPIに実装したとします。

@GetMapping("/customers")
public List<Customer> getAllCustomers(){
   return repository.findAll();       
}

顧客オブジェクトが前の例と同じ場合、すでに必要以上の情報を表示しています。idや、お気に入りのcoffeesの一覧まで必要でしょうか。顧客に自宅住所などの情報を追加すると、状況はさらに悪化します。

public class Customer {
   private Long id;
   private String firstName;
   private String lastName;
   private List<Coffee> coffees;
   Private String homeAddress;

}

エンドポイントでデータを絞り込まなければ、意図せず情報漏えいが発生します。多くの国では、フルネームと自宅住所の提供はプライバシー侵害にあたるためです。DTOを使ってAPIとデータモデルを分離していれば、マッピングを行うコントローラーがDTOに必要なフィールドだけを設定するため、こうした事態を防げます。後からドメインモデルに情報を追加した場合でも、DTOを使えば個人を特定できる情報が不用意に漏れるのを防げます。

public class CustomerDTO {
   private String firstName;
   private String lastName;
}

推奨事項

JavaでDTOを使ってサブシステムを分離するのは、一般的に有効な方法です。エンジニアリングの観点では、異なるレイヤー間のラウンドトリップを減らせます。また、セキュリティの観点では、意図しないデータ漏えいの防止に役立ちます。セキュリティのためには、DTOを用途ごとに作成し、再利用を最小限に抑えることをおすすめします。複数の機能でDTOを再利用する場合は、変更前に、そのDTOがどこで使われているかを把握しておきましょう。

基本として、DTOは簡潔に保ち、可能であればビジネスロジックを含めず、特定の機能に必要なデータだけを提供します。最後に、DTOには不変性が適していると考えています。Java 16で正式リリースされたJavaのレコードは、JavaでDTOを実装する優れた方法です。

Javaセキュリティについて詳しく知りたい方は、次のリソースをご覧ください。

無料のオンラインJavaコードチェッカーツールもお試しください。Snyk Codeエンジンが、コードのセキュリティと品質に関する問題をどのように分析するか確認できます。

よくある質問

DTOとは何ですか?

DTOはData Transfer Object(データ転送オブジェクト)の略です。アプリケーション内の異なるプロセス間でデータを受け渡すオブジェクトを指します。DTOはデータを集約することで、クライアントとサーバー間のメソッド呼び出し回数を減らす目的で設計されています。また、アプリケーションのドメインモデルとプレゼンテーション層など、エンティティ間の密結合を防ぎます。

JavaにおけるDTOとは何ですか?

JavaのData Transfer Objectは、通常、ビジネスロジックを持たないPOJO(Plain Old Java Object)です。一般に、使用するメソッド間のデータ受け渡しという目的に合わせた、フラットなデータ構造を持ちます。JavaのDTOは、privateフィールドとgetterメソッド、setterメソッドを備えたJavaBeanの規約に従うことがありますが、必須ではありません。

DTOはPOJOと同じものですか?

簡単に言えば、DTOはPOJOですが、すべてのPOJOがDTOというわけではありません。Data Transfer Objectは、プロセス間でデータを受け渡すための特定のPOJOです。

DTOを使うのは良いプラクティスですか?

はい。適切に実装されたDTOは、密結合とメソッド呼び出しを減らし、セキュリティを高めてデータ漏えいを防ぎます。

DAOとDTOの違いは何ですか?

DAOはData Access Object(データアクセスオブジェクト)の略で、ビジネスロジックと永続化層を分離するパターンで使われます。DAOは、更新、削除、保存などのCRUD操作によく使われます。一方、DTOはサブシステム間でデータを受け渡すことだけを目的としたオブジェクトであり、ビジネスロジックを含めるべきではありません。

Capture the Flagを始めよう

オンデマンドのバーチャル入門ワークショップを見て、Capture the Flagの課題の解き方を学びましょう。

続きを読む

blog feature ai lilac
Blog

エージェント型修正で脆弱性バックログをプルリクエストに変える

Evo Remediation Agentがオープンプレビューで利用可能になり、Evo Agentic AppSecに依存関係の自動解決機能が加わりました。AppSecチームは継続的な修正キャンペーンを実施できるようになりました

AI Background min crop
Blog

AIが、あなたが受け入れてきたあらゆるリスクを一気に顕在化させる:AI Security Panel: Unfilteredから得た7つの洞察

AIによって攻撃はより安価かつ高速になり、組織がすでに抱えているリスクが露呈しています。4人のセキュリティリーダーが、機械の速度に対応する防御と迅速な修復が今不可欠である理由を語ります。

blog feature ai violet
Blog

VulnBump:AIエージェントはビルドを壊さずに脆弱な依存関係を修正できるか?

VulnBumpでは、実際の依存関係のアップグレード307件をAIエージェントで検証します。脆弱性が解消され、ビルドとテストが正常に通った場合にのみ、修正成功と判定します。