🎄 科学と神々株式会社 アドベントカレンダー 2025
License System Day 4: 商用グレードのアーキテクチャ設計パターン
📖 今日のテーマ
基礎編の最終回です。今日は「どのように設計するか?」というアーキテクチャ設計を学びます。
商用グレードのシステムは、セキュリティ、スケーラビリティ、メンテナンス性を考慮した設計が必要です。
🏗️ 基本アーキテクチャ: クライアント・サーバー型
全体像
┌──────────────────┐ HTTPS/TLS ┌──────────────────┐
│ │◄────────────────────────►│ │
│ クライアント │ │ サーバー │
│ (アプリ/CLI) │ 1. アクティベーション │ (認証・認可) │
│ ├──────────────────────────►│ │
│ │ │ │
│ │ 2. ライセンスキー │ │
│ │◄──────────────────────────┤ │
│ │ │ │
│ │ 3. サービス利用 │ │
│ ├──────────────────────────►│ │
│ │ + ライセンスキー │ │
│ │ │ │
│ │ 4. レスポンス + 署名 │ │
│ │◄──────────────────────────┤ │
│ │ │ │
└──────────────────┘ └──────────────────┘
│ │
│ │
▼ ▼
公開鍵検証 秘密鍵署名
(改ざん検知) (データ保護)
│
▼
┌──────────────┐
│ データベース │
│ - Users │
│ - Licenses │
│ - Logs │
└──────────────┘
🎯 設計の3大原則
1. セキュリティ・バイ・デザイン
「後から追加」ではなく「最初から組み込む」
❌ 悪い例:
1. 機能を実装
2. テスト
3. リリース
4. 「あ、セキュリティ忘れた!」← 手遅れ
✅ 良い例:
1. セキュリティ要件定義
2. セキュアな設計
3. セキュアな実装
4. セキュリティテスト
5. リリース
2. 最小権限の原則
「必要最低限の権限だけを与える」
例: データベースアクセス
❌ すべてのユーザーが全テーブルにアクセス
→ データ漏洩のリスク
✅ 各ユーザーは自分のデータのみアクセス
→ 被害を最小化
3. 防御の多層化
「一つが破られても、次の防御がある」
Layer 1: HTTPS 暗号化
↓ (破られたとしても)
Layer 2: JWT 署名検証
↓ (破られたとしても)
Layer 3: レートリミット
↓ (破られたとしても)
Layer 4: 異常検知・ブロック
→ 一つの防御に頼らない!
🔐 セキュアな設計パターン
パターン1: 非対称鍵暗号
従来の方法(対称鍵):
同じ鍵で暗号化・復号化
❌ 鍵を安全に共有できない
非対称鍵暗号:
秘密鍵: サーバーのみ保持 → 署名作成
公開鍵: クライアントに配布 → 署名検証
✅ 鍵を共有する必要がない!
具体例: ECDSA P-256
// サーバー側(署名)
const crypto = require('crypto');
const sign = crypto.createSign('SHA256');
sign.update(JSON.stringify(response));
const signature = sign.sign(privateKey, 'base64');
// クライアント側(検証)
const verify = crypto.createVerify('SHA256');
verify.update(JSON.stringify(response));
const isValid = verify.verify(publicKey, signature, 'base64');
if (!isValid) {
throw new Error('改ざんされています!');
}
パターン2: JWT (JSON Web Token)
構造:
Header.Payload.Signature
例:
eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.
eyJ1c2VyX2lkIjoiMTIzIiwicGxhbiI6InByZW1pdW0ifQ.
MEUCIQDz7...
特徴:
✅ Stateless(サーバーで状態を持たない)
✅ 改ざん検知可能
✅ 有効期限設定可能
パターン3: レートリミット
目的:
DDoS 攻撃防止
API 乱用防止
サーバー負荷軽減
実装:
プラン別に制限設定
┌─────────────┬─────────┐
│ Free │ 10/時間 │
│ Premium │ 1000/時間│
│ Enterprise │ 無制限 │
└─────────────┴─────────┘
保存先:
- メモリ(Redis など)
- データベース(SQLite など)
📊 データベース設計
階層構造
Users (ユーザー)
├─ user_id
├─ email
├─ password_hash ← 平文保存は絶対NG!
└─ created_at
↓ 1対多
Subscriptions (サブスクリプション)
├─ subscription_id
├─ user_id
├─ plan_type (free/premium/enterprise)
├─ status (active/expired/cancelled)
└─ end_date
↓ 1対多
Licenses (ライセンス)
├─ license_id
├─ subscription_id
├─ activation_key (JWT)
├─ client_id (デバイス識別)
└─ validation_count
正規化の重要性
❌ 悪い設計(正規化なし):
Users テーブルに全部詰め込む
├─ user_id
├─ email
├─ plan_type
├─ license_key
└─ ...
問題:
- データの重複
- 更新時の不整合
- スケールしにくい
✅ 良い設計(正規化):
Users / Subscriptions / Licenses を分離
利点:
- データの一貫性
- 柔軟な拡張
- クエリの最適化
🚀 スケーラビリティの考え方
垂直スケーリング vs 水平スケーリング
垂直スケーリング(Scale Up):
サーバーのスペックを上げる
CPU: 2コア → 8コア
RAM: 4GB → 32GB
限界:
物理的な上限がある
コストが指数関数的に増加
水平スケーリング(Scale Out):
サーバーの台数を増やす
1台 → 10台 → 100台
利点:
理論上無限に拡張可能
一台あたりのコストは一定
設計での考慮点
✅ Stateless 設計
サーバーに状態を持たせない
→ どのサーバーでも同じ処理が可能
→ ロードバランサーで振り分け
✅ データベースの分離
アプリケーションサーバーとDBを分離
→ それぞれ独立にスケール
✅ キャッシュの活用
Redis などでキャッシュ
→ DB アクセスを減らす
🔄 API 設計のベストプラクティス
RESTful 設計
リソース指向:
GET /api/v1/users/:id ユーザー取得
POST /api/v1/users ユーザー作成
PUT /api/v1/users/:id ユーザー更新
DELETE /api/v1/users/:id ユーザー削除
認証:
POST /api/v1/auth/login ログイン
POST /api/v1/auth/logout ログアウト
ライセンス:
POST /api/v1/license/activate アクティベーション
GET /api/v1/license/validate 検証
POST /api/v1/license/deactivate 無効化
バージョニング
URL ベース:
/api/v1/...
/api/v2/...
利点: 分かりやすい
欠点: URL が増える
ヘッダーベース:
X-API-Version: 1
X-API-Version: 2
利点: URL がきれい
欠点: ドキュメント化が必要
エラーレスポンス
{
"error": {
"code": "LICENSE_EXPIRED",
"message": "ライセンスの有効期限が切れています",
"details": {
"expired_at": "2025-01-01T00:00:00Z",
"plan": "premium"
},
"actions": [
{
"label": "更新する",
"url": "/api/v1/license/renew"
}
]
}
}
🛡️ セキュリティチェックリスト
通信レベル
✅ HTTPS/TLS 1.3 の使用
✅ 証明書の検証
✅ 暗号スイートの選択
✅ HSTS ヘッダーの設定
アプリケーションレベル
✅ パスワードのハッシュ化(bcrypt/scrypt)
✅ SQL インジェクション対策
✅ XSS 対策
✅ CSRF 対策
✅ レートリミット
✅ 入力バリデーション
データベースレベル
✅ 最小権限のユーザー
✅ プリペアドステートメント
✅ バックアップの暗号化
✅ アクセスログの記録
📈 モニタリング・ロギング
何を記録するか
アクセスログ:
- タイムスタンプ
- IP アドレス
- エンドポイント
- レスポンスコード
- レスポンス時間
エラーログ:
- エラーの種類
- スタックトレース
- ユーザー情報
- 再現手順
ビジネスログ:
- ライセンス発行
- プラン変更
- 支払い情報
アラート設定
即座に対応が必要:
- サーバーダウン
- エラー率 > 5%
- レスポンス時間 > 3秒
早めの対応が必要:
- ディスク使用率 > 80%
- CPU 使用率 > 70%
- 不正アクセスの兆候
🎯 商用システムの事例研究
事例1: Stripe
特徴:
- API ファーストの設計
- 明確なエラーメッセージ
- 豊富なドキュメント
- Webhook による通知
学べること:
- 開発者体験の重要性
- テストモードの提供
- バージョン管理の戦略
事例2: Twilio
特徴:
- 使用量ベースの課金
- リアルタイムダッシュボード
- 多言語SDK
学べること:
- 透明性の高い料金体系
- 段階的な学習パス
- コミュニティサポート
🌟 まとめ
商用グレードの設計で重要なこと:
-
セキュリティ・バイ・デザイン
- 最初から組み込む
- 多層防御
-
スケーラビリティ
- Stateless 設計
- 水平スケーリング
-
API 設計
- RESTful
- バージョニング
- 明確なエラー
-
モニタリング
- ログ記録
- アラート設定
- 継続的な改善
🎓 理解度チェック
- 非対称鍵暗号の利点は?
- JWT の構造は?
- 垂直・水平スケーリングの違いは?
- なぜ Stateless 設計が重要?
💡 次回予告
Day 5: 暗号化の基礎 - 対称鍵と非対称鍵
セキュリティ基礎編に突入!
- 暗号化の歴史
- 対称鍵暗号の仕組み
- 非対称鍵暗号の仕組み
- 実際のコード例
お楽しみに!
前回: Day 3: なぜライセンス認証が必要なのか
次回: Day 5: 暗号化の基礎 - 対称鍵と非対称鍵
Happy Learning! 🎉