torutkのブログ

ソフトウェア・エンジニアのブログ

Java読書会「セキュア・バイ・デザイン」を読む会(第3回)開催

本日、Java読書会BOF主催の「セキュア・バイ・デザイン」を読む会(第3回)を開催しました。

本日は、第4章安全性を確立する実装テクニックの4.1不変性(immutability)から読み始めました。

読書メモ
  • クラスが可変となる設計をすると、排他制御が必要で完全性(integrity)が損なわれるので、不変な設計をしましょう

    • 不変性の実装は、JDK 17以降は record が使えるのでは?
  • 契約による設計で、速やかな失敗を

    • ホーアさん、凄い人
    • assertはあるけどあまり使われていないね。組み込み系ではassert使うけど(言語はJavaではないけど)
    • Androidでは、アノテーションで引数に@NonNullなど指定することが多い。
      • これはコンパイル時にチェックするので実行時にはチェックされないのでは?
  • 受け取った不正なデータをそのままログに出さない

    • なぜ? 9章によると、ログを読み込む別システム(ログ管理システム)が不正データを含むログを読み込みXSSになると
    • 開発観点ではログに不正データがないと問題切り分けが困難になるが安全性の観点が優先されるべき(この本の趣旨)
  • 妥当性の確認リスト

    • オリジン(発生源)の確認
    • データ・サイズの確認
    • 字句的内容(lexical content)の確認
    • 構文(syntax)の確認
    • データに対する意味(semantics)の確認
  • データサイズの確認は、10億桁の文字列を受け取って次の確認(正規表現エンジン等)に読み込ませるといったことを防ぐ意味がある

  • データに対する意味の確認は、この本ではドメイン・モデルで行うのが適していると著者は言っている

  • ドメイン・プリミティブはドメイン駆動設計で紹介された値オブジェクトをセキュリティを意識しながら改良したもので、セキュリティ問題の可能性をコードから取り除く

    • 不変条件を持ち、オブジェクト生成時に不変条件を確認
    • その存在だけでその値が有効であることを保証する厳格な定義
    • 操作メソッドをユーティリティクラスに出さない(振る舞いを変えた時にユーティリティクラスの操作を変更忘れる問題を防ぐため)
    • 冊数をintではなくQuantityクラス(ドメイン・プリミティブ)で正確かつ厳格に表現(冊数の有効範囲:例えば1冊以上200冊以下、を保証)
    • Quantityクラスに intを返すvalue()メソッドがあるが、これを出してしまうとドメインプリミティブが不完全にならないか?
      • Quantityの外部で、複数のQuantityインスタンスからvalue()でintで受け取った値同士を演算すると不変条件が破られていまう
  • メールアドレスの大文字・小文字で盛り上がる(書籍から脱線)

今日は、脱線した議論が多く、盛り上がりましたが読み進んだページ数は平均を下回っていました。

ドメイン・プリミティブの実装をrecordで再実装してみる

この本は、原書が2021年に出版されていますが、Javaのコード例はJava SE 8の範囲で書かれています。 Javaでは、Java SE 17においてrecordが正式導入されたので、値オブジェクトの実装が少ない記述量で実装できるようになっています。そこで、書籍のドメイン・プリミティブの例をrecordで再実装してみます。

Quantityクラスの例(リスト5.1よりコード抜粋、コメントは私が補足)

public final class Quantity {
    private final int value;

    public Quantity(final int value) {
        inclusiveBetween(1, 200, value);  // commons.lang の Validateクラスのstaticメソッドで範囲チェック
        this.value = value;
    }

    public int value() {
        return value;
    }
    
    pubilc Quantity add(final Quantity addend) {
        notNull(addend);  // commons.lang の Validateクラスのstaticメソッドでnullチェック
        return new Quantity(value + addend.value);
    }
    // ...略(equalsメソッド、hashCodeメソッド、toStringメソッドが略されていると思われる)
}

hashcodeやequalsメソッドは「...略」のところに現れるものと想定しておきます。 このクラスをrecord型で記述すると

public record Quantity(int value) {
    public Quantity {
        inclusiveBetween(1, 200, value)
    }

    public Quantity add(final Quantity addend) {
        notNull(addend);
        return new Quantity(value + addend.value);
    }
}

とシンプルに記述でき、フィールドの定義、コンストラクタの定義のうちフィールドへの代入、フィールドのgetterメソッド、equalsメソッド、hashCodeメソッド、toStringメソッドが自動で定義されます。

JDK 19リリースがもうすぐ

読書会開催時の雑談で、もうすぐ JDK 19 がリリースされると話題に上がりました。 JDK 19はどんな機能が入るのだろう?ということでリリースノートを見ると次のような機能が入ります。

JEPでは

JEP以外の更新では(抜粋)

  • System.outとSystem.err用のプロパティ stdout.encoding、stderr.encoding
  • Unicode 14.0
  • 追加のDate-Time Formats。DateTimeFormatter.ofLocalizedPattern("yMMM")などが記述可能

今月9月30日の夜に、JJUG主催ナイトセミナーでJDK 19の紹介があります。

jjug.doorkeeper.jp