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:アクセス制御と監視
