【書評】Secure by Design ── セキュリティはデザインで解決する
はじめに
「セキュリティは後から追加できる」と思ったことはないでしょうか。
現場でそういった判断を何度も見てきましたし、セキュリティ関連タスクが常に後回しになる現場も珍しくありません。
本書 Secure by Design(著:Dan Bergh Johnsson、Daniel Deogun、Daniel Sawano、Manning出版)は、その発想を根本から問い直す一冊です。セキュリティを「機能」として追加するのではなく、「設計」の問題として捉え直すことで、自然にセキュアなコードが生まれる、というアプローチを提唱しています。
本書の主張:セキュリティは懸念事項であって機能ではない
本書の出発点は明快です。
セキュリティを機能の集合として扱うのではなく、対処すべき懸念事項として理解する方が有益である。
1854年のスウェーデンの銀行強盗の話が冒頭に登場します。銀行は高品質な錠前を導入していましたが、金庫の蝶番が弱かったために簡単に破られてしまいます。「セキュリティ機能を実装した」という事実が、本質的な安全性を保証しなかった典型例です。
現代のソフトウェアでも同じことが起きています。ログイン機能を実装したのに、URLを直接叩けば認証なしで写真にアクセスできてしまう、といったケースです。本書はこれを「機能重視のセキュリティ」の限界として批判し、横断的な懸念事項として設計レベルで対処することを勧めます。
セキュリティ上の懸念事項として、本書は CIA-T を定義しています。
- C (Confidentiality) ── 機密性:情報を秘密に保つこと
- I (Integrity) ── 完全性:情報が不正に変更されないこと
- A (Availability) ── 可用性:必要なときにデータにアクセスできること
- T (Traceability) ── 追跡可能性:誰がいつ何にアクセスしたかを把握すること
従来アプローチの欠点
「開発者全員がセキュリティを常に意識して実装する」という従来アプローチは、次の理由から機能しないと著者らは述べています。
- セキュリティは常にビジネス機能より優先度が低くなる ビジネス機能のほうが収益に直結するため、どうしても後回しになります。
- 全員がセキュリティの専門家になれるわけではない JVMのパフォーマンス専門家やUXデザイナーが全員いないのと同じです。
- 未知の攻撃手法には対処できない 既知の脆弱性にしか対処できず、「知らないことを知る」ことは構造的に不可能です。
ドメイン駆動設計(DDD)との接続
本書の核心は、DDDとセキュリティの接続です。第3章で詳しく解説されており、以下の概念が後の章の基盤となります。
モデルとは何か
DDDにおけるモデルは「現実の厳密な単純化」です。手荷物システムで靴のサイズを持つ必要はなく、重さを持つことは重要です。モデリングは、何を省略するかの選択であり、その選択の質がシステムの安全性に直結します。
ユビキタス言語
ドメインエキスパートと開発者が共通の言語を使ってコミュニケーションすることで、用語の曖昧さから生まれるバグやセキュリティ上の欠陥を防ぎます。
要件定義書ではドメインモデルの用語を必ず使用してください。ドメインモデルの用語で表現するのが難しいものは、ソフトウェアとして記述するのも難しいでしょう。
境界付きコンテキストとセマンティクス境界
「注文」という言葉が、出荷部門と財務部門で異なる意味を持つ場合があります。このコンテキスト境界を無視してモデルを共有すると、セキュリティ上の問題が発生します。著者らが示す実例では、共有モデルを用いたことで「予約金額 ≥ 全価格の合計」という不変条件が破られ、セキュリティインシデントに発展しています。
セマンティクス境界を越えるデータは、概念の意味が暗黙的に変わる可能性があるため、セキュリティの観点から特に重要です。
ドメインプリミティブ:本書の核心テクニック
第5章で紹介されるドメインプリミティブは、本書で最も実践的な概念です。
定義が非常に正確で、存在するだけでその有効性が明らかになる値オブジェクトは、ドメイン プリミティブです。
問題:裸の文字列や整数
// Before:誰でも何でも渡せる
void register(String phoneNumber) { ... }
// addItem:数量に負の値も渡せてしまう
public void addItem(String isbn, int qty) { ... }
第2章で登場するオンライン書店のケーススタディでは、qtyに負の値を渡すことで、割引クーポンを不正に入手できてしまう欠陥が紹介されています。
解決:ドメインプリミティブの導入
public final class Quantity {
private final int value;
public Quantity(final int value) {
inclusiveBetween(1, 200, value); // 作成時に不変条件を強制
this.value = value;
}
public Quantity add(final Quantity addend) {
notNull(addend);
return new Quantity(value + addend.value);
}
}
Quantityオブジェクトが存在する時点で、値が1〜200の範囲にあることが保証されています。セキュリティを意識せず、ドメインの自然な表現をしたら、結果として安全になったというのが本書の主張です。
同様に、Usernameには英数字4〜40文字という制約を持たせることで、XSSに使われる<script>タグを入力できなくなります。セキュリティではなく「ユーザー名とは何か」という問いに答えた結果です。
Read-Once Objects:機密データの保護
パスワードやSSNのような機密データにはRead-Once Objectパターンが有効です。
- 値を一度しか取得できない
-
toString()でマスクされる - シリアライゼーションを禁止する
本番環境でTomcatがセッションをディスクに永続化するタイミングで、SSNが平文でディスクに書き出されていた、という実例が紹介されており、このパターンがあれば例外によって早期に検出できたと述べています。
XMLとBillion Laughs攻撃:設計の多層防御
第1章では、XMLのエンティティ展開を悪用したBillion Laughs攻撃が取り上げられています。1KB以下のXMLが、パース時に数GBのメモリを消費する攻撃です。
著者らの対処方針は「多層防御」です。
- パーサー設定:エンティティ展開を無効化する(柵を作る)
- 語彙スキャン:必要な要素を含むXMLのみを受け付ける(鍵をかける)
- 運用上の制約:メモリ制限でリソース暴走を防ぐ(番犬を置く)
これこそがセキュア・バイ・デザインの真髄です。設計をセキュアなソフトウェア開発のための主要なツールとマインドセットとして活用することです。
そのほかのトピック
本書後半では、以下のトピックが扱われています。
- 第6章:状態の完全性の確保 集約ルートによる不変条件の維持
- 第7章:状態の複雑さの軽減 状態遷移を明示化することでの安全性向上
- 第9章:障害の安全な処理 バルクヘッドパターンとChaos Engineeringの観点
- 第10章:クラウド思考のメリット イミュータブルなインフラとセキュリティ
- 第12〜13章:レガシーコードとマイクロサービスへの適用
読んでみて
本書を読んで最も印象的だったのは、「セキュリティを考えずに良い設計をしようとした結果、セキュリティが向上した」という逆説的な主張が、具体的なコードとケーススタディによって説得力を持って展開されている点です。
セキュリティを特別な知識として切り出さず、ドメインモデリングとDDDの自然な延長として位置づけている点は、多くのエンジニアが実践に移しやすい視点だと思います。
Java主体のコード例が多いものの、考え方はKotlinにもそのまま適用できます。特にKotlinのデータクラスとinitブロックを組み合わせたドメインプリミティブは相性が良く、すぐに試したくなりました。
設計やアーキテクチャに興味があるエンジニアには、読んでおく価値がある一冊です。
書籍情報
- タイトル:Secure by Design
- 著者:Dan Bergh Johnsson、Daniel Deogun、Daniel Sawano
- 出版社:Manning Publications
- O'Reilly Learning:Secure by Design