前置き
前回の以下の記事の続きになっています。
多層防御の城郭アナロジー
下図のように、1つのマイクロサービスコンポーネントが内部に複数(簡単のために2つとする)のドメイン境界で分割された論理モジュール(この境界がドメイン駆動における集約の範囲)を持つ状況を想定します。
このとき外部サービスは、図のように、このサービスコンポーネント内のすべてのモジュールを使用するのではなく、一部を使用するという状況だとします。
この時、インターフェイス分離の原則 に従うと、サービスコンポーネントは、各モジュールごとのインターフェイスをAPIとして外部のサービスに公開するため、ビジネス上多目的なサービスコンポーネントということになるはずです。
この場合の最小権限の原則を満たす、多層防御の実装はどうなるでしょうか?
1. The Outer Wall:インフラ層セキュリティ (サイドカーなど)
これは城の最も外側にある城壁と堀です。
役割
「そもそも誰がこの城の敷地内に入れるか」 という、最も粗い粒度でのアクセス制御を行います。
実装
・mTLS
信頼できるサービス(同盟軍)からの通信しか受け付けないようにします。
・ネットワークポリシー
特定のIPアドレスやサービスからのアクセスのみを許可します。
・WAF (Web Application Firewall)
明らかな攻撃パターン(SQLインジェクションなど)を入口でブロックします。
最小権限の原則
ここでは、「信頼できないネットワークからのアクセス」という最も広い権限を剥奪しています。
2. The Lobby Security:アプリケーション横断の関心事
これは城の本丸の入口にいる衛兵です。
役割
城の敷地内に入ってきた 人物(リクエスト)が、何者であるかを認証し、基本的な認可情報を確認します。
実装
・認証
JWTトークンの検証などを行い、リクエストの主が誰であるかを確認します。
・共通の認可
トークンに含まれるロールやスコープを読み取り、「この人物は一般市民か、それとも管理者か」といった大まかな権限を確立します。
・共通ロギング・レートリミット
すべてのアクセスを記録し、過剰なアクセスを制限します。
最小権限の原則
ここでは、「認証されていない、あるいは基本的な役割を持たないユーザー」が内部のどのモジュールにもアクセスする権限を剥奪しています。
3. The Secure Room:モジュールごとのセキュリティ機能
これが、最も重要な部分です。
これは本丸内部にある、「宝物庫」と「食糧庫」のそれぞれの扉にある、異なる鍵です。
役割
衛兵のチェックを通過した人物が、特定の部屋(モジュール) に対して、特定の操作を行う権限があるかを、最終的かつ最も厳密にチェックします。
実装 (例)
モジュールA (宝物庫 - OrderAdminModule)
衛兵が確認したロールが「管理者」でなければ、アクセスを拒否する。
たとえ管理者であっても、「読み取り」は許可するが、「削除」は特定の条件下でしか許可しない、といったビジネスロジックに基づいた詳細な権限チェックを行う。
モジュールB (食糧庫 - OrderReaderModule)
ロールが「一般市民」でも、「自分自身の注文情報」に限り読み取りを許可する(リソースベースの認可)。
他人の注文情報へのアクセスは、たとえリクエストが来ても拒否する。
最小権限の原則
ここで初めて、真の最小権限が達成されます。
ユーザーは、認証され、かつ特定のビジネスコンテキスト(モジュール)において、必要最小限の操作を行う権限のみを与えられます。
「食糧庫に入る鍵」では、決して「宝物庫」を開けることはできません。
結論
大事なのは、セキュリティを「一律の壁」として捉えるのではなく、インフラからアプリケーション、そして個別のビジネスコンテキストへと、段階的に権限を絞り込んでいくという点です。
上記で提案したセキュリティアーキテクチャは、以下の点で保守が行いやすいです。
関心の分離
インフラ、共通アプリ、個別ビジネスロジックという、異なる関心事のセキュリティを明確に分離しています。
効率性と堅牢性の両立
共通のセキュリティ機能は再利用して効率化しつつ、最も重要なビジネスロジックの守りは、そのドメインの文脈に閉じて堅牢に実装できています。
ドメインとセキュリティ境界の一致
ドメイン駆動設計における論理的な境界(集約の範囲)が、そのままセキュリティ上の権限境界と一致しているため、システムは非常に理解しやすく、保守しやすくなります。
まさしく、これが
最小権限の原則を体現した、理想的な多層防御アーキテクチャ
と言えるでしょう。
段階的なセキュリティアーキテクチャの進化
上記のようなセキュリティアーキテクチャ歯確かに理想的です。
もちろん、最初から「2つのモジュールに共通したセキュリティ機能が何なのか?」が自明なら、最速で上記のようなセキュリティアーキテクチャにできるでしょう。
でも、現実はそううまくいかないです。
最初から完璧な共通機能を見抜くことは実質不可能です。
そのため、初期はあえてDRY原則に反しているかもしれないことを許容して、
着実に共通のセキュリティ機能をあぶり出し、後から共通化する
という風に、進化させるのが賢明です。
このプロセスは、独立した村々がコミュニケーションを取りながら、一つの大きな都市へと発展していく様に非常に似ています。そのアナロジーで見ていきましょう。
ステップ1: 自己完結した村の誕生(個別実装)
状況
最初は、2つの村(モジュールA、モジュールB)がそれぞれ独立して存在します。
共通のインフラ(土地)は共有していますが、生活に必要な機能はすべて自分たちの村の中で完結させています。
何が共通のセキュリティ機能なのか?がまだ不明確
実装
村Aは自分たちのための井戸を掘り、自分たちのための見張り台を建てます。
村Bも同様に、自分たちの井戸と見張り台を独自に建設します。
メリットとデメリット
・スピードと自律性 vs DRY違反
他の村との調整なしに、素早く必要な機能を実装できる。
ただし、その代わりに共通の横断的関心機能に気づきにくくなり、DRY原則に反している可能性は高いです。
・コンテキストへの最適化 vs 個別最適化
自分たちの村の地形や文化に最適化された井戸や見張り台を作れます。
ただし、それは同時にもう1つのモジュールとの全体目線では、個別最適に走っていることになりえます。
ステップ2: 交流とパターンの発見(継続的コミュニケーション)
状況
村同士の交流が始まります。
隣の文脈とのギルド間連携がまさにこれに該当します。
村長たち(各モジュールの開発チーム)は定期的に会合を開き、お互いの村の運営について話し合います。
発見
話し合ううちに、彼らは気づきます。
「我々はどちらも、ほぼ同じ設計の井戸を、ほぼ同じ苦労をして掘っている。見張り台の作り方もそっくりだ。これはDRYに反しており、無駄ではないか?」
トリガー
ここで 「Rule of Three(3回ルールの原則)」 が適用されることがよくあります。
3つの村が同じようなものを作っていたら、それは共通化すべき強力なサインです。
ステップ3:共同インフラの建設(共通機能の抽出)
状況
村長たちは、共通の課題を解決するために協力することを決定します。
この時に、2つの村(モジュールチーム)がコラボレーションすることになるので、ファシリテーターをしてくれる、イネイブラーさんを1人立てることを推奨します。
実装
DRYの適用
個別の井戸を掘るのをやめ、両方の村からアクセスしやすい場所に、より大きくて高性能な 共同の貯水池(共通認証ライブラリ) を建設します。
責務の移譲
個別の見張り台を廃止し、都市全体を見渡せる 中央の城壁とゲート(API Gatewayでの共通認可) を建設します。
このゲートの警備は、専門の衛兵チーム(プラットフォームチーム)に任せます。
メリット
一貫性と効率性
すべての住民が同じ品質の水を使えるようになり、建設コストも削減できます。
専門化
各村は、井戸掘りや見張り番といった共通の雑務から解放され、自分たちの村の特産品(ビジネスロジック)作りに集中できます。
なぜこの進化的なアプローチが重要なのか
早期からの抽象化は、ろくなことを招きません。
初期のわずかなセキュリティ機能の重複コストを許容することで、
将来にわたって保守しやすく、本当に必要な共通基盤を、データと実績に基づいて構築する
という、極めて賢明な投資戦略なのです。








