ストーリー
高橋アーキテクトが架空のシステム要件書を配りました。
課題概要
| 項目 | 内容 |
|---|---|
| 対象システム | オンライン医療予約システム |
| 課題数 | 5つ |
| 合計時間 | 90分 |
| 評価基準 | 各課題の成果物を作成すること |
対象システムの概要
- 患者がWebブラウザから医療機関の予約を行う
- 医師が自分のスケジュールと予約を管理する
- 管理者がユーザーやシステム設定を管理する
- 外部の決済サービス(Stripe)と連携する
- 診療記録は暗号化して保存する
課題1: DFD(データフロー図)を作成しよう(20分)
以下の要素を含むDFDを作成してください。
要件
- 外部エンティティ:患者、医師、管理者、決済サービス
- プロセス:認証サーバー、予約API、決済API、通知サービス
- データストア:ユーザーDB、予約DB、診療記録DB
- 信頼境界を明確にする
解答例(自分で作成してから確認しよう)
─ ─ ─ ─ 信頼境界(外部) ─ ─ ─ ─
┌──────┐ ┌──────────┐
│ 患者 │──HTTPS──→┌───────────┐ │ 決済 │
└──────┘ │ API │──API Call──→ │ サービス │
┌──────┐ │ Gateway │ │ (Stripe) │
│ 医師 │──HTTPS──→│ │ └──────────┘
└──────┘ └─────┬─────┘
┌──────┐ │
│ 管理者 │──HTTPS──→ │
└──────┘ ─ ─ ─ ─ ─ ┼ ─ ─ ─ 信頼境界(内部) ─ ─ ─
│
┌────────▼─────────┐
│ 認証サーバー │
└────────┬─────────┘
│
┌─────────────┼─────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ 予約API │ │ 決済API │ │ 通知 │
│ │ │ │ │ サービス │
└─────┬─────┘ └─────┬─────┘ └───────────┘
│ │
┌─────▼─────┐ ┌─────▼─────┐
│ ユーザーDB │ │ 予約DB │
│ 診療記録DB │ └───────────┘
└───────────┘
信頼境界:
- 外部境界: インターネット ↔ API Gateway
- 内部境界: API Gateway ↔ 内部サービス群
- データ境界: サービス ↔ データベース
ポイント:
- 信頼境界を3層で定義(外部、内部、データ)
- 外部エンティティと内部プロセスを明確に分離
- 決済サービスは外部エンティティとして扱う
課題2: STRIDE分析を実施しよう(20分)
DFDの各コンポーネントに対してSTRIDE分析を行い、主要な脅威を3つ以上特定してください。
分析対象
- 予約APIプロセス
- 認証サーバー
- 診療記録DB
解答例(自分で作成してから確認しよう)
予約APIプロセスのSTRIDE分析:
| カテゴリ | 脅威 | 説明 |
|---|---|---|
| S(なりすまし) | 他の患者になりすまして予約 | 認証トークンの窃取 |
| T(改ざん) | 予約料金の改ざん | リクエストパラメータの変更 |
| R(否認) | 予約キャンセルの否認 | 患者が「キャンセルしていない」と主張 |
| I(情報漏洩) | 他の患者の予約情報閲覧 | IDOR脆弱性 |
| D(DoS) | 大量予約リクエストで予約システムダウン | ボットによる自動予約 |
| E(権限昇格) | 患者が医師の管理機能にアクセス | 認可チェックの不備 |
認証サーバーのSTRIDE分析:
| カテゴリ | 脅威 | 説明 |
|---|---|---|
| S(なりすまし) | ブルートフォースでのログイン突破 | パスワード総当り |
| T(改ざん) | JWTトークンの改ざん | 弱い署名アルゴリズム |
| I(情報漏洩) | エラーメッセージからのユーザー存在確認 | ユーザー列挙攻撃 |
診療記録DBのSTRIDE分析:
| カテゴリ | 脅威 | 説明 |
|---|---|---|
| T(改ざん) | 診療記録の不正な変更 | DBへの直接アクセス |
| I(情報漏洩) | 暗号化されていない診療記録の漏洩 | バックアップファイルの流出 |
| D(DoS) | DBの過負荷によるサービス停止 | 大量クエリ攻撃 |
課題3: DREAD評価を行おう(15分)
課題2で特定した脅威の中から上位3つを選び、DREADスコアリングを行ってください。
解答例(自分で作成してから確認しよう)
| 脅威 | D(被害) | R(再現) | E(悪用) | A(範囲) | D(発見) | 平均 | 優先度 |
|---|---|---|---|---|---|---|---|
| 診療記録の情報漏洩 | 10 | 7 | 5 | 9 | 6 | 7.4 | High |
| IDOR による予約情報漏洩 | 7 | 9 | 8 | 6 | 8 | 7.6 | High |
| ブルートフォース攻撃 | 7 | 9 | 7 | 3 | 9 | 7.0 | High |
分析の根拠:
- 診療記録は医療情報(HIPAA対象)のため被害が最大
- IDORは再現性が高く、自動ツールで発見しやすい
- ブルートフォースはツールで自動化可能だが影響は個別ユーザー
課題4: セキュリティ要件を定義しよう(20分)
DREAD評価の結果に基づき、優先度の高い脅威に対するセキュリティ要件を3つ定義してください。
解答例(自分で作成してから確認しよう)
SEC-DATA-001: 診療記録の暗号化
- カテゴリ: データ保護
- STRIDE: I(情報漏洩)
- DREADスコア: 7.4
- 受け入れ基準:
- 診療記録はAES-256-GCMで暗号化して保存する
- 暗号化キーはAWS KMSで管理する
- 復号はアプリケーション経由でのみ可能
- DBの直接参照では暗号文のみ表示される
- 優先度: Must
SEC-AUTHZ-001: 予約情報のアクセス制御
- カテゴリ: 認可
- STRIDE: I(情報漏洩)/ E(権限昇格)
- DREADスコア: 7.6
- 受け入れ基準:
- 患者は自分の予約のみ閲覧・変更可能
- 医師は自分に関連する予約のみ閲覧可能
- 他の患者の予約IDでアクセスすると403エラー
- 全APIエンドポイントで所有者チェックを実施
- 優先度: Must
SEC-AUTH-001: ブルートフォース対策
- カテゴリ: 認証
- STRIDE: S(なりすまし)
- DREADスコア: 7.0
- 受け入れ基準:
- 5回連続失敗でアカウントを30分間ロック
- IPベースのレートリミット(1分間に10回まで)
- ログイン失敗を監査ログに記録
- CAPTCHA(3回失敗後に表示)
- 優先度: Must
課題5: セキュアな設計パターンを適用しよう(15分)
課題4で定義したセキュリティ要件を実現するための設計パターンを選択し、適用方法を記述してください。
解答例(自分で作成してから確認しよう)
// 1. ゲートキーパーパターン:全リクエストの認証・認可チェック
const securityMiddleware = [
rateLimiter({ windowMs: 60000, max: 60 }),
helmet(),
authenticate,
auditLogger,
];
// 2. 入力バリデーションパイプライン
const appointmentSchema = z.object({
doctorId: z.string().uuid(),
date: z.string().datetime(),
reason: z.string().min(1).max(500),
});
// 3. リソースの所有者チェック(IDORの防止)
const ownershipCheck = (resourceType: string) => {
return async (req: Request, res: Response, next: NextFunction) => {
const resource = await getResource(resourceType, req.params.id);
if (!resource) return res.status(404).json({ error: "Not found" });
const canAccess =
resource.patientId === req.user.id ||
resource.doctorId === req.user.id ||
req.user.role === "admin";
if (!canAccess) {
return res.status(403).json({ error: "Forbidden" });
}
next();
};
};
// 4. 診療記録の暗号化
const encryptMedicalRecord = (record: string, key: Buffer): string => {
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
const encrypted = Buffer.concat([
cipher.update(record, "utf8"),
cipher.final(),
]);
const tag = cipher.getAuthTag();
return `${iv.toString("hex")}:${tag.toString("hex")}:${encrypted.toString("hex")}`;
};
適用パターンの対応:
| セキュリティ要件 | 適用パターン |
|---|---|
| 診療記録の暗号化 | エンベロープ暗号化 |
| 予約情報のアクセス制御 | ゲートキーパー + 所有者チェック |
| ブルートフォース対策 | トークンバケット + アカウントロック |
達成度チェック
| 課題 | テーマ | 完了 |
|---|---|---|
| 課題1 | DFDの作成 | [ ] |
| 課題2 | STRIDE分析 | [ ] |
| 課題3 | DREAD評価 | [ ] |
| 課題4 | セキュリティ要件定義 | [ ] |
| 課題5 | セキュアな設計パターン適用 | [ ] |
まとめ
| ポイント | 内容 |
|---|---|
| DFD | データの流れと信頼境界を可視化する |
| STRIDE | 6カテゴリで漏れなく脅威を洗い出す |
| DREAD | 定量評価で優先度を決定する |
| 要件定義 | 脅威分析の結果を実装可能な要件に変換する |
チェックリスト
- DFDを作成して信頼境界を定義できた
- STRIDE分析で脅威を体系的に特定できた
- DREADスコアリングで優先度を判定できた
- セキュリティ要件をSMART原則で定義できた
- 適切な設計パターンを選択して適用できた
次のステップへ
お疲れさまでした。脅威モデリングの実践演習が完了しました。 次はStep 2のチェックポイントです。
推定所要時間: 90分