mcpField
A
api-security-best-practices

api-security-best-practices

github.com/sickn33セキュリティ48.7k8.9k 回インストール今日更新
リポジトリ

api-security-best-practices について

API を開発・レビューする人向けのセキュリティガイドです。認証、認可、入力検証、濫用対策、ログを扱い、実際の ID 管理とデータモデルに沿った確認を重視します。ガイドは完了済みの監査ではありません。

主な機能

  1. 01トークン署名とクレームの検証
  2. 02ユーザーとテナント単位の認可
  3. 03入力検証とリクエスト本文の制限
  4. 04レート制限と濫用対策の設計
  5. 05パスワード、シークレット、ログの安全な扱い

活用シーン

  1. 01保護された API エンドポイントのレビュー
  2. 02ユーザー間・テナント間アクセスの調査
  3. 03トークン更新フローの強化
  4. 04入力検証と制限機能の障害テスト

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/sickn33/agentic-awesome-skills --skill api-security-best-practices

インストール後、Claude Code で/api-security-best-practicesを入力して読み込まれたか確認します。

詳細

API セキュリティのベストプラクティス

呼び出し元の ID から認可、検証済み入力、ストレージ、観測可能なレスポンスに至るまで、リクエストの境界をレビューします。2 つ目の認証システムを導入するのではなく、アプリケーションの実際の ID プロバイダーとデータモデルを維持してください。

使用する場面

保護されたエンドポイントを追加するとき、オブジェクトアクセスをレビューするとき、寛容なリクエスト解析を置き換えるとき、または API の悪用経路を調査するときに使用します。具体的な欠陥については、失敗しているルートとその呼び出し元から始めてください。無関係なセキュリティインフラストラクチャをデプロイしないでください。

入力と前提条件

ルート、呼び出し元/テナントモデル、ID プロバイダー、トークン契約、ランタイムとロックされた依存関係のバージョン、データベーススキーマ、プロキシトポロジ、認可されたテストスコープを記録します。テスト環境では合成 ID を使用してください。既存のタスク認可はそのまま引き継がれます。本番スキャン、アカウント書き込み、メッセージ送信にはそれぞれ独自の権限が必要です。以下の Node の例は Express、jsonwebtoken、Zod の統合スケッチです。アプリケーション/データベースのアダプターは、完全に実行可能なサービスとして提示するのではなく、意図的に名前だけを示しています。統合する前に、インストールされているバージョンに対して API を確認してください。

1. 正確なトークン契約を認証する

確立されたプロバイダー/セッションミドルウェアを優先してください。サービスが HMAC JWT 契約を所有する場合は、強力なサーバー所有の鍵、固定アルゴリズム、正確な issuer と audience、および必須のランタイムクレームを要求します。署名検証の前に、デコードされたトークンから権限を推測しないでください。呼び出し元が選択した検証アルゴリズムを決して受け入れないでください。

const jwt = require('jsonwebtoken');

// Illustrative first-party access-token contract; not a third-party OAuth adapter.
const ACCESS_POLICY = {
  algorithms: ['HS256'], issuer: 'example-auth', audience: 'example-api'
};
function verifyAccessToken(token, signingKey) {
  const claims = jwt.verify(token, signingKey, ACCESS_POLICY);
  if (!claims || typeof claims !== 'object' ||
      typeof claims.sub !== 'string' || !claims.sub ||
      typeof claims.tenantId !== 'string' || !claims.tenantId ||
      !Number.isSafeInteger(claims.exp) || !Number.isSafeInteger(claims.iat) ||
      claims.exp <= claims.iat) {
    throw new Error('Invalid access claims');
  }
  return { subject: claims.sub, tenantId: claims.tenantId };
}
function readBearer(header) {
  if (typeof header !== 'string' || header.length > 8192) return null;
  const match = /^Bearer ([A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+)$/i.exec(header);
  return match ? match[1] : null;
}

同じ issuer/audience/algorithm と、アプリケーションで承認された短い有効期限でアクセストークンを発行します。検証失敗は、トークンや例外をそのまま返さず、一般的な 401 として処理してください。有効期限だけではトークンは失効しません。実際の脅威モデルに応じて、失効または短命セッションを定義してください。非対称プロバイダー鍵を使用するサービスには、この HMAC の例ではなく、プロバイダーのディスカバリ/JWKS 検証と鍵ローテーションポリシーが必要です。アクセストークンをリフレッシュトークンとして再利用しないでください。

セッションのリフレッシュ

プロバイダーがサポートするセッションフロー、またはサーバー側の不透明なリフレッシュ設計を使用します。保存するのはダイジェスト、有効期限、ユーザー/セッションファミリー、失効状態のみです。1 つのアトミックなトランザクションで、古いアクティブトークンを消費し、代替トークンを作成します。同時再利用で 2 つの後継トークンを発行してはなりません。定義された再利用処理は、影響を受けるファミリーを失効させます。新しいアクセストークンを発行するときは、現在のユーザーステータスと権限を確認してください。リフレッシュを意図したクライアント/セッションにバインドし、Cookie ベースのリクエストを CSRF から保護します。トークンをログに記録したり、データベースに平文で保存したり、URL 経由でリフレッシュトークンを返したりしないでください。フローが完了したとみなす前に、同時リフレッシュ、有効期限、リプレイ、失効、トランザクション失敗をテストしてください。ここにはデータベーストランザクションアダプターは同梱されていません。

2. リソースと操作を認可する

認証は呼び出し元を識別し、認可はオブジェクトとテナントに対する正確な操作を決定します。ロール名が自動的にクロステナントアクセスを許可するわけではありません。check-then-write の競合を避けるために、データベースのミューテーションで所有者/テナントの述語を適用し、書き込み可能なプロパティを許可リストに登録してください。製品のリソース開示ポリシーに沿って、404/403 を一貫して使用します。

// Prisma-style sketch; id and tenant types must match your actual schema.
async function deleteOwnedPost(prisma, postId, principal) {
  const result = await prisma.post.deleteMany({
    where: { id: postId, userId: principal.subject, tenantId: principal.tenantId }
  });
  return result.count === 1;
}

管理者パスには、明示的に分離されたポリシーと監査イベントが必要です。すべての所有者チェックに暗黙の管理者バイパスを追加しないでください。有効なユーザーが別のユーザーのオブジェクトにアクセスする場合、別のテナントの同じ ID、削除されたメンバーシップ、バルクエンドポイントをテストしてください。

3. 一度だけパースし、検証済みの値を使用する

部分的な数値パースを拒否し(12abc は ID 12 ではありません)、安全でない整数、予期しない プロパティ、大きすぎるリクエストを拒否します。パラメータ化されたデータベースクエリを使用してください。ORM は ビジネス上の認可を提供するものではなく、安全でない生の SQL を安全にするものでもありません。

function parsePositiveId(raw) {
  if (typeof raw !== 'string' || !/^[1-9][0-9]{0,15}$/.test(raw)) return null;
  const value = Number(raw);
  return Number.isSafeInteger(value) && value > 0 ? value : null;
}

const { z } = require('zod');
const profileUpdate = z.object({
  displayName: z.string().trim().min(1).max(100)
}).strict();
function validateBody(schema) {
  return (req, res, next) => {
    const parsed = schema.safeParse(req.body);
    if (!parsed.success) {
      return res.status(400).json({ error: 'Invalid request' });
    }
    req.validatedBody = parsed.data; // Defaults/transforms must reach the handler.
    next();
  };
}
// Handler uses req.validatedBody, never the original body or an arbitrary spread.

パース前にボディサイズの制限を設定してください。Zod による形状検証はあくまで一つの層です。トランザクション内で 現在の所有者、許可された状態遷移、一意性を確認してください。HTML については、メンテナンスされているサニタイザーを使って 必要なタグと属性のみを許可し、その後、出力先の安全な出力 API でレンダリングしてください。プレーンなコメントについては プレーンテキストを優先してください。文字列をサニタイズしても、あらゆる JavaScript、URL、HTML のコンテキストで 安全になるわけではありません。上流の API レスポンスも信頼できない入力として検証してください。

送信 URL については、許可されるスキームとホスト、リダイレクトの挙動、解決される IP レンジ、認証情報のポリシー、 タイムアウト、レスポンスサイズを定義してください。正規表現や URL パーサーだけでは SSRF や DNS リバインディングを 防げません。ファイルアップロードも同様に、タイプとコンテンツのチェック、サイズ制限、分離されたストレージ、 読み取り時の認可が必要です。

4. DDoS 対策を謳わずに悪用を制御する

既存のゲートウェイとメンテナンスされているレート制限ストアを使用してください。認証済みユーザーのキーを導出する前に 認証を行い、ユーザーが指定したティアを決して信頼しないでください。匿名トラフィックについては、ライブラリがサポートする IPv6 対応の IP キー処理を使用し、Express の trust proxy を実際に信頼できるホップに設定してください。すべての呼び出し元に対して 盲目的に有効化しないでください。

分散クォータには、ストア障害時の挙動を定義したアトミックなカウンターと有効期限の実装を使用してください。 手書きの INCR に続く EXPIRE は避けてください。その間のクラッシュで永続的なキーが残る可能性があります。 ユーザーごとのクォータ、IP ごとの悪用対策、同時実行数の制限、上流サービスの予算を区別してください。実際の ウィンドウとリセットのセマンティクスを記録し、ハードコードされたフルウィンドウ値ではなく正確な Retry-After を送信してください。 インメモリのリミッターは、共有ストアが設定されていない限りプロセスごとです。

同時リクエスト、IPv4/IPv6、偽造された転送ヘッダー、不明なティア、ID の欠如、Redis の障害、有効期限をテストしてください。 アプリケーションのレート制限はネットワークの飽和を吸収できません。Helmet は HTTP レスポンスヘッダーを構成するものであり、 DDoS 対策でもアクセス制御でも、上流の容量制御の代替でもありません。CSP/HSTS は実際のデプロイとサブドメインポリシーに対して ロールアウトしてください。プリロード設定を盲目的にコピーしないでください。

5. パスワード、シークレット、ログ

可能な限り、確立された ID プロバイダーを使用してください。保存されるパスワードには、パラメータを調整した メンテナンスされているパスワードハッシュ方式を使用してください(新規設計では Argon2id を推奨)。漏洩したパスワードや 一般的なパスワードをチェックし、パスフレーズをサポートしてください。恣意的な大文字・記号の構成ルールを課したり、 長いパスワードを黙って切り詰めたりしないでください。レガシーな bcrypt には入力バイト数の制限があり、移行時に 明示的に考慮する必要があります。パスワードの文字数と UTF-8 バイト数は異なります。

シークレットは承認されたシークレット機構に保管し、起動時に値を出力せずに必要な設定を確認し、露出した認証情報を ローテーションしてください。通常のログに生のトークン、パスワード、リクエストボディ、完全なデータベースの例外を含めないでください。 適切な保持とアクセスのポリシーの下で、範囲を限定したイベント名、リクエストの相関、安全なステータス・エラークラスを ログに記録してください。サニタイズされたエラーは、マスアサインメントされたユーザーオブジェクトを返すべきではありません。 CORS はブラウザのクロスオリジンアクセスを制御するものであり、API 認証でも CSRF 対策でもありません。

実例: プロフィール更新の境界

文字列 ID と編集可能な表示名を持つ PATCH /users/:id を前提とします。

  1. 認可された呼び出し元/テナントと現在のエンドポイントの挙動をフィクスチャに記録します。
  2. 欠落/期限切れ/対象オーディエンス不一致のトークンが、ストレージアクセスの前に 401 を返すことを確認します。
  3. 12abc、安全でない整数、空の名前、および余分な role フィールドを送信し、400 が返り データベースが変更されないことを期待します。前後に空白のある有効な名前を送信し、パース後にトリムされた 値のみが所有者およびテナントでスコープされた更新に到達することを確認します。
  4. 別のユーザーの有効な ID とクロステナントの ID を試し、文書化された拒否 と変更なしを期待します。有効な所有者のリクエストは許可されたプロパティのみを更新します。
  5. クォータ/ストアの障害を発生させ、ログにリクエストトークンや名前が含まれないことを確認します。

ルートポリシー、再現された失敗ケース、正確なテストコマンド/出力、および 残るギャップを返します。これらは対象アプリケーションで実行することが期待されるチェックであり、 このリポジトリがデプロイ済み API をテスト済みであるという主張ではありません。

制限事項

このガイドは完全な ID サービス、認定されたセキュリティ監査、またはペネトレーション テストではありません。スニペットはアプリケーションアダプターおよび統合エラーミドルウェアを省略しています。パーサーの 単体テストは、データベーストランザクション、プロキシの挙動、プロバイダーセッションを検証しません。 未テストのルート、テナントパス、失敗モードを明示的に報告してください。構造チェックの通過や リスクメタデータラベルから、クリーンなセキュリティ態勢を推測しないでください。

参考文献

コメント