LESSON 25分

ストーリー

佐藤CTO
脅威を分析し、リスクを評価できるようになった。次は設計の原則
佐藤CTO
Defense in Depth
佐藤CTO
城を守るのと同じだ。堀、城壁、見張り塔、門番 — 一重の防御では心もとない。複数の層で守る。これが多層防御(Defense in Depth)だ
あなた
もし外壁が破られても、内壁で食い止められるということですね
佐藤CTO
そうだ。セキュリティアーキテクチャには、長年の経験から導き出された原則がある。これを知らずに設計すると、必ず穴ができる

多層防御(Defense in Depth)

多層防御は、複数のセキュリティ層を重ねることで、1つの層が破られても他の層で防御する戦略です。

多層防御の7つの層

対象対策例
物理層データセンター、サーバー入退室管理、監視カメラ
ネットワーク層通信経路ファイアウォール、IDS/IPS、VPN
境界層DMZ、エッジWAF、DDoS防御、CDN
アプリケーション層Webアプリ、API入力検証、認証・認可、CSRF対策
データ層データベース、ファイル暗号化、アクセス制御、バックアップ
ホスト層OS、コンテナパッチ管理、ハードニング、EDR
ポリシー層組織、人セキュリティ教育、インシデント対応計画
// 多層防御をExpressミドルウェアで実装する例
import express from 'express';
import helmet from 'helmet';
import rateLimit from 'express-rate-limit';
import cors from 'cors';

const app = express();

// 層1: HTTPヘッダーセキュリティ(境界層)
app.use(helmet());
app.use(helmet.contentSecurityPolicy({
  directives: {
    defaultSrc: ["'self'"],
    scriptSrc: ["'self'"],
    styleSrc: ["'self'", "'unsafe-inline'"],
    imgSrc: ["'self'", 'data:'],
  },
}));

// 層2: CORS制御(境界層)
app.use(cors({
  origin: ['https://app.example.com'],
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  credentials: true,
}));

// 層3: レート制限(ネットワーク層)
app.use(rateLimit({
  windowMs: 15 * 60 * 1000,  // 15分
  max: 100,                    // 最大100リクエスト
  standardHeaders: true,
  legacyHeaders: false,
}));

// 層4: 入力検証(アプリケーション層)
app.use(express.json({ limit: '10kb' }));  // ペイロードサイズ制限

// 層5: 認証(アプリケーション層)
app.use('/api', authenticateMiddleware);

// 層6: 認可(アプリケーション層)
app.use('/api/admin', authorizeMiddleware('admin'));

// 層7: 監査ログ(ポリシー層)
app.use(auditLogMiddleware);

最小権限の原則(Principle of Least Privilege)

ユーザーやプロセスには、必要最小限の権限だけを付与します。

適用対象Before(違反)After(準拠)
IAMロールAdministratorAccessを全員に付与役割ごとにカスタムポリシーを作成
DBアクセスrootユーザーでアプリケーション接続読み取り専用・書き込み専用ユーザーを分離
APIスコープ*(全権限)のAPIキーを発行必要なエンドポイントだけに限定
ファイル権限chmod 777chmod 644(オーナーだけ書き込み可能)
コンテナrootユーザーで実行非rootユーザーで実行、読み取り専用FS
// 最小権限の原則を適用したIAMポリシー例
const developerPolicy = {
  Version: '2012-10-17',
  Statement: [
    {
      Effect: 'Allow',
      Action: [
        's3:GetObject',
        's3:PutObject',
      ],
      Resource: 'arn:aws:s3:::app-uploads/*',
      Condition: {
        StringEquals: {
          's3:x-amz-server-side-encryption': 'aws:kms',
        },
      },
    },
    {
      Effect: 'Allow',
      Action: [
        'dynamodb:GetItem',
        'dynamodb:PutItem',
        'dynamodb:Query',
      ],
      Resource: 'arn:aws:dynamodb:ap-northeast-1:*:table/app-*',
    },
    // 明示的に拒否: 本番環境のリソースへのアクセス
    {
      Effect: 'Deny',
      Action: '*',
      Resource: '*',
      Condition: {
        StringEquals: {
          'aws:ResourceTag/Environment': 'production',
        },
      },
    },
  ],
};

フェイルセーフデフォルト(Fail-Safe Defaults)

システムがエラーや障害に遭遇した場合、安全な状態にフォールバックする原則です。

原則説明
デフォルト拒否明示的に許可されない限りアクセスを拒否ファイアウォールのデフォルト拒否ルール
安全な障害処理エラー時に情報を漏洩しないスタックトレースを非表示にする
タイムアウトの設定無期限のセッションを避けるセッションの有効期限を設定
安全な初期状態新規リソースはデフォルトで制限的S3バケットのデフォルト非公開
// フェイルセーフデフォルトの実装例

// 悪い例: デフォルト許可
function checkAccess(user: User, resource: Resource): boolean {
  if (user.role === 'blocked') {
    return false;  // ブロックされたユーザーだけ拒否
  }
  return true;     // それ以外は全員許可(危険)
}

// 良い例: デフォルト拒否
function checkAccess(user: User, resource: Resource): boolean {
  const allowedRoles = resource.allowedRoles;
  if (!allowedRoles || allowedRoles.length === 0) {
    return false;  // 許可ロールが未定義ならデフォルト拒否
  }
  return user.roles.some(role => allowedRoles.includes(role));
}

// エラーハンドリング: 安全な障害処理
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
  // 内部エラーの詳細を外部に漏らさない
  securityLogger.error('Unhandled error', {
    error: err.message,
    stack: err.stack,
    path: req.path,
    userId: req.user?.id,
  });

  // ユーザーには汎用的なメッセージだけ返す
  res.status(500).json({
    error: 'Internal Server Error',
    requestId: req.id,  // 問い合わせ用のIDだけ提供
  });
});

職務の分離(Separation of Duties)

1つの重要な操作を、複数の人や役割に分けることで不正を防止する原則です。

分離対象BeforeAfter
デプロイ権限開発者が直接本番にデプロイ開発者がPRを作成 → 別の人がレビュー → CI/CDが自動デプロイ
DB操作開発者がSQL直接実行変更はマイグレーションスクリプトとして管理 → DBAがレビュー
秘密情報管理1人が全てのシークレットを管理暗号化キーとデータを別々の管理者が保持
コードレビュー自分のコードを自分でマージ別の開発者のApproveが必須

セキュリティ・バイ・デザイン(Security by Design)

セキュリティを後付けではなく、設計段階から組み込む考え方です。

設計段階で考慮すべきセキュリティ要件

// セキュリティ要件を型で表現する例
interface SecureEndpointConfig {
  // 認証要件
  authentication: {
    required: boolean;
    methods: ('jwt' | 'apiKey' | 'oauth2')[];
    mfaRequired: boolean;
  };

  // 認可要件
  authorization: {
    model: 'RBAC' | 'ABAC';
    requiredRoles?: string[];
    requiredPermissions?: string[];
  };

  // 入力検証
  inputValidation: {
    schema: object;          // JSON Schema or Zod schema
    maxPayloadSize: string;  // e.g., '10kb'
    sanitize: boolean;
  };

  // レート制限
  rateLimit: {
    windowMs: number;
    maxRequests: number;
    keyBy: 'ip' | 'userId' | 'apiKey';
  };

  // 監査
  audit: {
    logRequest: boolean;
    logResponse: boolean;
    sensitiveFields: string[];  // ログに含めないフィールド
  };

  // データ保護
  dataProtection: {
    encryptResponse: boolean;
    cacheControl: string;
    cors: {
      allowedOrigins: string[];
    };
  };
}

まとめ

ポイント内容
多層防御複数のセキュリティ層を重ねて単一障害点を排除
最小権限の原則必要最小限の権限だけを付与し、過剰な権限を避ける
フェイルセーフデフォルトエラー時は安全な状態にフォールバック、デフォルト拒否
職務の分離重要な操作を複数の役割に分けて不正を防止
セキュリティ・バイ・デザイン設計段階からセキュリティを組み込む

チェックリスト

  • 多層防御の7つの層を説明できる
  • 最小権限の原則をIAMポリシーやアプリケーション設計に適用できる
  • フェイルセーフデフォルトの考え方を実装に反映できる
  • 職務の分離を開発プロセスに適用できる
  • セキュリティ・バイ・デザインの考え方を理解した

次のステップへ

次は「演習:脅威分析を実施しよう」です。ここまで学んだSTRIDE、リスクアセスメント、セキュリティアーキテクチャの原則を使って、実際のWebアプリケーションに対する脅威分析を実施しましょう。


推定読了時間: 25分