API におけるデータ露出:予防と検出

API を通じた不適切なデータ露出は、現代のシステムにおける主要な脆弱性の一つです。設定が誤っている、または保護が不十分な API は、機密情報を漏洩させ、ユーザーデータを危険にさらし、LGPD や GDPR などのプライバシー規制に違反するおそれがあります。

API におけるデータ露出の種類

1. 過剰なデータ露出(API3:2023 - OWASP)

必要以上の情報を返す API:

  • いくつかのフィールドのみが必要な場合に、エンドポイントがオブジェクト全体を返す
  • レスポンスへの機密フィールドの含有(パスワードハッシュ、内部 token)
  • エラーで露出するシステムメタデータ
      // [ERRO] Exemplo RUIM - Expondo dados demais
      GET /api/users/123
      {
      "id": 123,
      "name": "João Silva",
      "email": "[email protected]",
      "password_hash": "$2b$10$...",  // [AVISO] Nunca expor
      "ssn": "123-45-6789",            // [AVISO] Dado sensível
      "internal_role_id": 42,          // [AVISO] Dado interno
      "created_at": "2025-01-01",
      "last_login_ip": "192.168.1.1"   // [AVISO] Informação sensível
      }
      // [OK] Exemplo BOM - Apenas dados necessários
      GET /api/users/123/profile
      {
      "id": 123,
      "name": "João Silva",
      "avatar_url": "https://..."
      }
      

2. リソースフィルタリングの欠如(Broken Object Level Authorization)

ユーザーは ID を変更することで他のユーザーのデータにアクセスできます:

      // [ERRO] Vulnerável a IDOR (Insecure Direct Object Reference)
      GET /api/orders/456  // Usuário A pode ver pedidos do Usuário B
      // [OK] Protegido - Validar autorização
      app.get('/api/orders/:id', async (req, res) => {
      const order = await Order.findById(req.params.id);
      // Verificar se o pedido pertence ao usuário autenticado
      if (order.userId !== req.user.id) {
      return res.status(403).json({ error: 'Acesso negado' });
      }
      res.json(order);
      });
      

3. URL における機密情報の露出

  • クエリ文字列内の認証 token
  • パス内の個人データ
  • サーバーログ内の機密情報

予防戦略

Data Transfer Objects(DTOs)の実装

      // TypeScript - Definir claramente o que expor
      interface UserPublicDTO {
      id: number;
      name: string;
      avatar_url: string;
      }
      class UserService {
      async getPublicProfile(userId: number): Promise<UserPublicDTO> {
      const user = await db.users.findUnique({ where: { id: userId } });
      // Retornar apenas campos permitidos
      return {
      id: user.id,
      name: user.name,
      avatar_url: user.avatar_url
      };
      }
      }
      

シリアライゼーションフィルターの適用

  • class-transformer(TypeScript)などのライブラリを使用する
  • 機密フィールドをマークするためのデコレーター(@Exclude)
  • 異なるコンテキスト向けのシリアライゼーショングループ(公開 vs. admin)

認可の検証

      // Middleware de autorização
      const checkResourceOwnership = (resourceType) => {
      return async (req, res, next) => {
      const resource = await db[resourceType].findById(req.params.id);
      if (!resource) {
      return res.status(404).json({ error: 'Recurso não encontrado' });
      }
      if (resource.ownerId !== req.user.id && !req.user.isAdmin) {
      return res.status(403).json({ error: 'Acesso não autorizado' });
      }
      req.resource = resource;
      next();
      };
      };
      // Uso
      app.get('/api/documents/:id',
      authenticate,
      checkResourceOwnership('documents'),
      (req, res) => {
      res.json(req.resource);
      }
      );
      

検出技術

Payload 分析

  • 重要なエンドポイントのレスポンスを手動でレビューする
  • Burp Suite、OWASP ZAP などのツールを使用する
  • 露出したフィールドを検証する自動テスト

監視とアラート

  • 異常なアクセスパターンを検出する(enumeration)
  • 過剰な拒否アクセスについてアラートを出す
  • ユーザーごとに転送されるデータ量を監視する

Code Review と静的解析

  • DTOs とシリアライゼーションモデルをレビューする
  • 認可の実装を検証する
  • 露出を特定するために SAST ツールを使用する

ベストプラクティス

  • 露出するデータに最小権限の原則を採用する
  • スクレイピングを防止するために rate limiting を実装する
  • 連番 ID の代わりに UUID を使用する
  • 転送中および保存時の機密データを暗号化する
  • 安全な変更のために API のバージョン管理を実装する
  • 定期的にセキュリティテストを実施する
  • 各エンドポイントがどのデータを露出するかを明確に文書化する
  • GraphQL を慎重に使用する(over-fetching を助長する可能性がある)

コンプライアンスと規制

  • LGPD/GDPR:データ最小化、処理の法的根拠
  • PCI-DSS:クレジットカードデータの保護
  • HIPAA:健康情報の保護(米国)
  • SOC 2:アクセス制御と監視