EXERCISE 60分

ストーリー

佐藤CTO
理論はもう十分だ。実際にやってみよう

佐藤CTOがシステム構成図を広げました。

佐藤CTO
このEコマースプラットフォームの脅威分析をやってくれ。攻撃面の洗い出しから、STRIDE分析、リスクマトリクスの作成、対策の設計まで。一通り回してみろ
あなた
わかりました。Step 1で学んだことをフルに活かします

対象システム:ECプラットフォーム「ShopNow」

graph TD
    Users["ユーザー(Web/Mobile)"]
    Users -->|HTTPS| CDN["CDN + WAF<br/>(CloudFront)"]
    CDN --> Kong["API Gateway (Kong)"]

    Kong --> Auth["認証サービス<br/>(Auth0)"]
    Kong --> Product["商品サービス<br/>(Node)"]
    Kong --> Cart["カートサービス<br/>(Node)"]
    Kong --> OrderSvc["注文サービス<br/>(Java)"]
    Kong --> PaySvc["決済サービス<br/>(Node)"]

    Auth --> UsersDB[("Users<br/>(RDS)")]
    Product --> ProductsDB[("Products<br/>(RDS)")]
    Cart --> Redis[("Redis Cache")]
    OrderSvc --> OrdersDB[("Orders<br/>(RDS)")]
    PaySvc --> Stripe["Stripe API<br/>(外部サービス)"]

    classDef client fill:#6c757d,stroke:#495057,color:#fff
    classDef infra fill:#0d6efd,stroke:#0a58ca,color:#fff
    classDef service fill:#198754,stroke:#146c43,color:#fff
    classDef datastore fill:#f5a623,stroke:#c47d10,color:#fff
    classDef external fill:#e94560,stroke:#c23050,color:#fff

    class Users client
    class CDN,Kong infra
    class Auth,Product,Cart,OrderSvc,PaySvc service
    class UsersDB,ProductsDB,Redis,OrdersDB datastore
    class Stripe external

システム概要

コンポーネント技術スタックデータ
フロントエンドNext.js, Reactセッション情報
API GatewayKongルーティング、レート制限
認証サービスAuth0 + カスタムJWTユーザー認証情報、MFA設定
商品サービスNode.js + TypeScript商品情報、在庫データ
カートサービスNode.js + Redisカートデータ(一時)
注文サービスJava + Spring Boot注文履歴、配送情報
決済サービスNode.js + Stripe SDK決済トークン、取引記録
データベースAmazon RDS (PostgreSQL)各サービスの永続データ

Mission 1:攻撃面(Attack Surface)のマッピング(15分)

上記のシステム構成をもとに、以下の観点で攻撃面を洗い出してください。

要件

  1. 外部公開エンドポイントを全て列挙する
  2. 信頼境界を少なくとも4つ特定する
  3. 各境界を通過するデータの種類機密レベルを記述する
  4. 第三者連携ポイント(外部API)のリスクを評価する

出力フォーマット

interface AttackSurface {
  externalEndpoints: {
    endpoint: string;
    method: string;
    authRequired: boolean;
    dataClassification: 'Public' | 'Internal' | 'Confidential' | 'Restricted';
    description: string;
  }[];
  trustBoundaries: {
    id: string;
    from: string;
    to: string;
    protocol: string;
    dataTypes: string[];
    risks: string[];
  }[];
  thirdPartyIntegrations: {
    service: string;
    dataShared: string[];
    trustLevel: 'High' | 'Medium' | 'Low';
    failureImpact: string;
  }[];
}
解答例
const shopNowAttackSurface: AttackSurface = {
  externalEndpoints: [
    {
      endpoint: '/api/auth/login',
      method: 'POST',
      authRequired: false,
      dataClassification: 'Restricted',
      description: 'ユーザー認証エンドポイント。ブルートフォース攻撃の対象。',
    },
    {
      endpoint: '/api/auth/register',
      method: 'POST',
      authRequired: false,
      dataClassification: 'Confidential',
      description: 'ユーザー登録。スパムアカウント作成のリスク。',
    },
    {
      endpoint: '/api/products',
      method: 'GET',
      authRequired: false,
      dataClassification: 'Public',
      description: '商品一覧の取得。DoS攻撃の対象になりやすい。',
    },
    {
      endpoint: '/api/products/:id',
      method: 'GET',
      authRequired: false,
      dataClassification: 'Public',
      description: '商品詳細の取得。IDORの可能性。',
    },
    {
      endpoint: '/api/cart',
      method: 'POST/PUT/DELETE',
      authRequired: true,
      dataClassification: 'Internal',
      description: 'カート操作。価格改ざんのリスク。',
    },
    {
      endpoint: '/api/orders',
      method: 'POST',
      authRequired: true,
      dataClassification: 'Confidential',
      description: '注文作成。不正注文、在庫操作のリスク。',
    },
    {
      endpoint: '/api/orders/:id',
      method: 'GET',
      authRequired: true,
      dataClassification: 'Confidential',
      description: '注文詳細の取得。IDOR(他人の注文閲覧)のリスク。',
    },
    {
      endpoint: '/api/payments',
      method: 'POST',
      authRequired: true,
      dataClassification: 'Restricted',
      description: '決済処理。最も高いセキュリティが必要。',
    },
  ],
  trustBoundaries: [
    {
      id: 'TB-1',
      from: 'ユーザー(ブラウザ/モバイル)',
      to: 'CDN + WAF',
      protocol: 'HTTPS/TLS 1.3',
      dataTypes: ['認証トークン', 'ユーザー入力', 'セッションCookie'],
      risks: ['XSS', 'CSRF', 'セッションハイジャック', 'DDoS'],
    },
    {
      id: 'TB-2',
      from: 'API Gateway',
      to: '各マイクロサービス',
      protocol: 'HTTP(内部ネットワーク)',
      dataTypes: ['認証済みリクエスト', 'ユーザーデータ'],
      risks: ['サービス間通信の盗聴', '不正なサービス呼び出し', '権限昇格'],
    },
    {
      id: 'TB-3',
      from: 'マイクロサービス',
      to: 'データベース(RDS)',
      protocol: 'PostgreSQL over TLS',
      dataTypes: ['PII', '注文データ', '在庫データ'],
      risks: ['SQLインジェクション', '過剰なデータアクセス', '接続情報の漏洩'],
    },
    {
      id: 'TB-4',
      from: '決済サービス',
      to: 'Stripe API(外部)',
      protocol: 'HTTPS',
      dataTypes: ['決済トークン', '取引ID', '金額情報'],
      risks: ['APIキーの漏洩', '中間者攻撃', 'Stripe障害時の処理'],
    },
  ],
  thirdPartyIntegrations: [
    {
      service: 'Auth0',
      dataShared: ['メールアドレス', 'パスワードハッシュ', 'MFA設定'],
      trustLevel: 'High',
      failureImpact: '全ユーザーがログイン不可。ビジネス停止。',
    },
    {
      service: 'Stripe',
      dataShared: ['決済トークン', '取引金額', '顧客参照ID'],
      trustLevel: 'High',
      failureImpact: '決済不可。売上直接影響。',
    },
    {
      service: 'CloudFront (CDN)',
      dataShared: ['静的コンテンツ', 'APIリクエストのプロキシ'],
      trustLevel: 'High',
      failureImpact: 'サイトアクセス不可。全面障害。',
    },
  ],
};

ポイント: 攻撃面の洗い出しでは、認証不要のエンドポイントが最もリスクが高い。また、信頼境界を跨ぐデータフローには必ず検証と暗号化が必要。


Mission 2:STRIDE脅威モデリング(15分)

Mission 1で特定した攻撃面に対して、STRIDEの6カテゴリを適用し、脅威を洗い出してください。

要件

  1. 各STRIDEカテゴリについて、少なくとも2つの具体的な脅威を特定する
  2. 脅威ごとに攻撃シナリオを記述する
  3. 脅威が影響するコンポーネントを明記する
  4. 各脅威に対する初期の緩和策を提案する

出力フォーマット

interface StrideThreat {
  id: string;
  category: 'Spoofing' | 'Tampering' | 'Repudiation' | 'InformationDisclosure' | 'DoS' | 'ElevationOfPrivilege';
  title: string;
  attackScenario: string;
  affectedComponents: string[];
  impact: 'Critical' | 'High' | 'Medium' | 'Low';
  mitigation: string;
}
解答例
const strideThreats: StrideThreat[] = [
  // Spoofing(なりすまし)
  {
    id: 'S-001',
    category: 'Spoofing',
    title: 'JWTトークンの窃取によるなりすまし',
    attackScenario: 'XSS脆弱性を悪用してlocalStorageからJWTを窃取し、被害者のアカウントでAPI呼び出しを行う',
    affectedComponents: ['フロントエンド', 'API Gateway', '全サービス'],
    impact: 'Critical',
    mitigation: 'HttpOnly Cookie、SameSite属性、CSP、トークンローテーション、異常検知',
  },
  {
    id: 'S-002',
    category: 'Spoofing',
    title: 'Credential Stuffing攻撃',
    attackScenario: '漏洩したメール/パスワードリストを使い、ログインエンドポイントに大量のリクエストを送信',
    affectedComponents: ['認証サービス', 'Auth0'],
    impact: 'High',
    mitigation: 'アカウントロックアウト、CAPTCHA、MFA必須化、漏洩パスワードDB照合',
  },
  // Tampering(改ざん)
  {
    id: 'T-001',
    category: 'Tampering',
    title: 'カート内商品の価格改ざん',
    attackScenario: 'APIリクエストを改ざんし、カートに追加する商品の価格を0円に書き換える',
    affectedComponents: ['カートサービス', '注文サービス'],
    impact: 'Critical',
    mitigation: 'サーバーサイドでの価格再取得、署名付きカートデータ、整合性チェック',
  },
  {
    id: 'T-002',
    category: 'Tampering',
    title: 'レスポンスの改ざん(MITM)',
    attackScenario: 'TLS未設定の内部通信を傍受し、商品情報や注文ステータスを改ざんする',
    affectedComponents: ['API Gateway', '各マイクロサービス間'],
    impact: 'High',
    mitigation: 'サービス間mTLS、サービスメッシュ導入、レスポンス署名',
  },
  // Repudiation(否認)
  {
    id: 'R-001',
    category: 'Repudiation',
    title: '注文取消しの否認',
    attackScenario: 'ユーザーが注文後にキャンセルし「注文していない」と主張。ログが不十分で証明できない',
    affectedComponents: ['注文サービス', '決済サービス'],
    impact: 'Medium',
    mitigation: 'イベントソーシング、タイムスタンプ付き監査ログ、操作の電子署名',
  },
  {
    id: 'R-002',
    category: 'Repudiation',
    title: '管理者操作の否認',
    attackScenario: '管理者が商品価格を不正に変更し、操作履歴が残っていないため証跡なし',
    affectedComponents: ['商品サービス', '管理画面'],
    impact: 'High',
    mitigation: '全管理操作のイミュータブルな監査ログ、変更理由の必須入力、承認フロー',
  },
  // Information Disclosure(情報漏洩)
  {
    id: 'I-001',
    category: 'InformationDisclosure',
    title: 'エラーレスポンスによる内部情報の漏洩',
    attackScenario: 'APIが詳細なスタックトレースやDB情報をエラーレスポンスに含む',
    affectedComponents: ['全マイクロサービス'],
    impact: 'Medium',
    mitigation: '本番環境でのスタックトレース非表示、統一エラーフォーマット',
  },
  {
    id: 'I-002',
    category: 'InformationDisclosure',
    title: 'IDOR(Insecure Direct Object Reference)',
    attackScenario: '/api/orders/123 のIDを変更して他人の注文情報(住所、連絡先)を閲覧',
    affectedComponents: ['注文サービス'],
    impact: 'Critical',
    mitigation: '全エンドポイントでの所有者チェック、UUID使用、認可ミドルウェア',
  },
  // Denial of Service(サービス拒否)
  {
    id: 'D-001',
    category: 'DoS',
    title: 'API Gatewayへの大量リクエスト',
    attackScenario: 'ボットネットからAPI Gatewayに大量リクエストを送信し、正規ユーザーのアクセスを妨害',
    affectedComponents: ['API Gateway', 'CDN', '全サービス'],
    impact: 'High',
    mitigation: 'WAFルール、レート制限、オートスケーリング、CDNキャッシュ活用',
  },
  {
    id: 'D-002',
    category: 'DoS',
    title: 'Redisキャッシュの枯渇',
    attackScenario: '大量のカート操作でRedisメモリを枯渇させ、カートサービスを停止させる',
    affectedComponents: ['カートサービス', 'Redis'],
    impact: 'Medium',
    mitigation: 'TTL設定、メモリ上限アラート、evictionポリシー、ユーザーごとのカート数制限',
  },
  // Elevation of Privilege(権限昇格)
  {
    id: 'E-001',
    category: 'ElevationOfPrivilege',
    title: 'JWT claimsの改ざんによる権限昇格',
    attackScenario: 'JWTのペイロードを改ざんし、roleをadminに変更。署名検証が不十分な場合成功する',
    affectedComponents: ['API Gateway', '認証サービス'],
    impact: 'Critical',
    mitigation: 'RS256署名検証の厳格化、role情報はDB参照、JWTの短い有効期限',
  },
  {
    id: 'E-002',
    category: 'ElevationOfPrivilege',
    title: 'サービス間通信を悪用した権限昇格',
    attackScenario: '内部APIを直接呼び出し、API Gatewayの認証チェックをバイパスする',
    affectedComponents: ['各マイクロサービス'],
    impact: 'High',
    mitigation: 'ネットワークポリシーで内部API直接アクセスを禁止、サービス間認証',
  },
];

Mission 3:リスクマトリクスの作成(15分)

Mission 2で特定した脅威に対して、DREADスコアリングを適用し、リスクマトリクスを作成してください。

要件

  1. Mission 2の脅威から上位6つを選定する
  2. 各脅威にDREADスコアを付ける(各項目1-10)
  3. 総合スコアを計算し、リスクレベルを分類する
  4. 結果をリスクマトリクスとして図示する

出力フォーマット

interface DreadAssessment {
  threatId: string;
  title: string;
  damage: number;
  reproducibility: number;
  exploitability: number;
  affectedUsers: number;
  discoverability: number;
  totalScore: number;
  riskLevel: 'Critical' | 'High' | 'Medium' | 'Low';
  priority: number; // 1が最優先
}
解答例
const dreadAssessments: DreadAssessment[] = [
  {
    threatId: 'I-002',
    title: 'IDOR -- 他人の注文情報閲覧',
    damage: 9,
    reproducibility: 9,
    exploitability: 8,
    affectedUsers: 8,
    discoverability: 8,
    totalScore: 8.4,
    riskLevel: 'Critical',
    priority: 1,
  },
  {
    threatId: 'E-001',
    title: 'JWT claims改ざんによる権限昇格',
    damage: 10,
    reproducibility: 6,
    exploitability: 5,
    affectedUsers: 10,
    discoverability: 6,
    totalScore: 7.4,
    riskLevel: 'High',
    priority: 2,
  },
  {
    threatId: 'T-001',
    title: 'カート内商品の価格改ざん',
    damage: 8,
    reproducibility: 8,
    exploitability: 7,
    affectedUsers: 5,
    discoverability: 7,
    totalScore: 7.0,
    riskLevel: 'High',
    priority: 3,
  },
  {
    threatId: 'S-001',
    title: 'JWTトークン窃取によるなりすまし',
    damage: 9,
    reproducibility: 5,
    exploitability: 6,
    affectedUsers: 7,
    discoverability: 6,
    totalScore: 6.6,
    riskLevel: 'High',
    priority: 4,
  },
  {
    threatId: 'D-001',
    title: 'API GatewayへのDDoS攻撃',
    damage: 7,
    reproducibility: 9,
    exploitability: 7,
    affectedUsers: 10,
    discoverability: 3,
    totalScore: 7.2,
    riskLevel: 'High',
    priority: 5,
  },
  {
    threatId: 'E-002',
    title: 'サービス間通信のバイパス',
    damage: 8,
    reproducibility: 4,
    exploitability: 4,
    affectedUsers: 10,
    discoverability: 4,
    totalScore: 6.0,
    riskLevel: 'High',
    priority: 6,
  },
];

リスクマトリクス(可視化)

影響度 ↑
 極大 │         │  D-001  │  I-002  │
      │         │  E-002  │  E-001  │
  大  │         │         │  T-001  │
      │         │         │  S-001  │
  中  │         │         │         │
      │         │         │         │
  小  │         │         │         │
      └─────────┴─────────┴─────────→ 発生可能性
        低        中         高

Mission 4:対策設計書の作成(15分)

Mission 3で優先度が高いと判断した脅威に対して、具体的な対策を設計してください。

要件

  1. 上位3つの脅威に対する対策を詳細に設計する
  2. 各対策の実装方法をコードまたは設定で示す
  3. 対策の効果コストを見積もる
  4. 残留リスクを評価する
解答例

対策1:IDOR対策(I-002)

// 認可ミドルウェア: リソースの所有者チェック
import { Request, Response, NextFunction } from 'express';

interface AuthorizationRule {
  resource: string;
  ownerField: string;
  adminOverride: boolean;
}

function authorizeResourceOwner(rule: AuthorizationRule) {
  return async (req: Request, res: Response, next: NextFunction) => {
    const resourceId = req.params.id;
    const currentUserId = req.user!.id;
    const isAdmin = req.user!.roles.includes('admin');

    if (rule.adminOverride && isAdmin) {
      return next();
    }

    const resource = await getResource(rule.resource, resourceId);
    if (!resource) {
      return res.status(404).json({ error: 'Not Found' });
    }

    if (resource[rule.ownerField] !== currentUserId) {
      // セキュリティログに記録(不正アクセスの試み)
      securityLogger.warn('IDOR_ATTEMPT', {
        userId: currentUserId,
        resourceType: rule.resource,
        resourceId,
        timestamp: new Date().toISOString(),
      });
      return res.status(404).json({ error: 'Not Found' }); // 403ではなく404を返す
    }

    next();
  };
}

// 適用例
app.get('/api/orders/:id',
  authenticate,
  authorizeResourceOwner({ resource: 'orders', ownerField: 'userId', adminOverride: true }),
  orderController.getById
);

対策2:JWT改ざん防止(E-001)

// JWT検証の厳格化
import jwt from 'jsonwebtoken';
import jwksClient from 'jwks-rsa';

const client = jwksClient({
  jwksUri: 'https://auth.shopnow.com/.well-known/jwks.json',
  cache: true,
  rateLimit: true,
});

async function verifyToken(token: string): Promise<JwtPayload> {
  const decoded = jwt.decode(token, { complete: true });
  if (!decoded || decoded.header.alg !== 'RS256') {
    throw new Error('Invalid token algorithm');
  }

  const key = await client.getSigningKey(decoded.header.kid);
  const publicKey = key.getPublicKey();

  const payload = jwt.verify(token, publicKey, {
    algorithms: ['RS256'],   // HS256を許可しない
    issuer: 'https://auth.shopnow.com/',
    audience: 'shopnow-api',
    maxAge: '15m',           // 最大有効期間15分
  }) as JwtPayload;

  // roleはDBから取得(JWTのclaimsを信頼しない)
  const userRoles = await roleRepository.findByUserId(payload.sub);
  payload.roles = userRoles;

  return payload;
}

対策3:価格改ざん防止(T-001)

// サーバーサイドでの価格再計算
async function createOrder(cartId: string, userId: string): Promise<Order> {
  const cart = await cartService.getCart(cartId, userId);

  // クライアントから送られた価格を信頼しない
  const verifiedItems = await Promise.all(
    cart.items.map(async (item) => {
      const product = await productService.getProduct(item.productId);
      if (!product || !product.isAvailable) {
        throw new BusinessError(`商品 ${item.productId} は購入できません`);
      }
      return {
        productId: product.id,
        name: product.name,
        price: product.currentPrice,   // DB上の最新価格を使用
        quantity: item.quantity,
        subtotal: product.currentPrice * item.quantity,
      };
    })
  );

  const totalAmount = verifiedItems.reduce((sum, item) => sum + item.subtotal, 0);

  // 整合性チェック用のハッシュを付与
  const integrityHash = createHmac('sha256', ORDER_SIGNING_KEY)
    .update(JSON.stringify({ items: verifiedItems, totalAmount, userId }))
    .digest('hex');

  return orderRepository.create({
    userId,
    items: verifiedItems,
    totalAmount,
    integrityHash,
    status: 'pending',
  });
}

対策の効果とコスト

対策対象脅威工数(人日)リスク低減率残留リスク
IDOR対策(認可ミドルウェア)I-002395%Low
JWT厳格化(RS256 + DB参照)E-001290%Low
サーバーサイド価格再計算T-001298%Low
mTLS導入E-002585%Medium
WAF + レート制限強化D-001275%Medium
HttpOnly Cookie移行S-001380%Medium

評価基準

評価項目配点合格ライン
攻撃面の網羅性25%主要なエンドポイントと信頼境界を70%以上特定
STRIDE分析の精度25%各カテゴリで2つ以上の現実的な脅威を特定
リスク評価の妥当性25%DREADスコアの根拠が論理的
対策設計の実現性25%具体的なコード/設定で実装方法を示せている

推定読了時間: 60分