本ページはレビュー報告書の様式サンプルです。記載の指摘・コードは説明のための架空例であり、特定のシステムを診断した結果ではありません。

SAMPLE REPORT — 様式見本

セキュリティ・コード品質レビュー報告書(サンプル)

対象システム

不動産管理SaaS(サンプル)

レビュー範囲

管理会社向け管理画面/入居者向けアプリ

技術構成

Next.js/NestJS/PostgreSQL

期間

2026年X月X日〜X月X日(架空日程)

1サマリー(経営者向け)

1Critical
2High
3Medium
4Low

リリース前に対応を推奨する項目(架空例・優先順)

  1. 1テナント間の認可不足(IDOR)の是正 — 他社の契約情報が参照できる経路を塞ぐ
  2. 2APIキー等の秘密情報のクライアント露出の解消 — サーバー側のみで保持する構成へ変更
  3. 3検索系エンドポイントの入力検証追加 — 不正な値でのエラー応答の見直し

※本サマリーは「発見された問題の重大度と対応の優先順位」を示すものであり、リリース可否を保証するものではありません(本ページの数値・件数は説明用の架空例です)。

2指摘一覧(様式見本・3件を抜粋)

実際の報告書では、発見した指摘すべてをこの形式(重大度・再現手順・該当箇所・修正提案)で記載します。以下はフォーマットを示すための架空例です。

Critical

テナント間の認可不足(IDOR)

FINDING-01(架空例)

概要

契約詳細の取得APIにテナント(管理会社)の検証がなく、リクエスト中の契約IDを別テナントのものに書き換えると、他社の契約情報が取得できてしまう、という架空の想定です。

再現手順(架空例)

  1. 管理会社Aのアカウントでログインし、任意の契約詳細画面を開く
  2. リクエストのURLに含まれる contractId を、管理会社Bが所有する契約IDに書き換える
  3. レスポンスに管理会社Bの契約情報(入居者名・家賃等)が含まれることを確認する

該当箇所(架空のサンプルコード)

GET /api/contracts/:id のハンドラ — 所属テナントの検証なし

// 架空のサンプルコードです(実在のコードではありません)
export async function GET(req, { params }) {
  const contract = await db.contract.findUnique({
    where: { id: params.id },
  });
  return NextResponse.json(contract);
}

修正提案(架空のサンプルコード)

// 修正案(架空のサンプルコードです)
export async function GET(req, { params }) {
  const tenantId = await getTenantIdFromSession(req);
  const contract = await db.contract.findFirst({
    where: { id: params.id, tenantId },
  });
  if (!contract) {
    return NextResponse.json({ error: "Not Found" }, { status: 404 });
  }
  return NextResponse.json(contract);
}
重大度の理由(架空例):認証さえ通れば、ID推測のみで他社の個人情報・金銭記録に到達できる想定であり、影響範囲・再現の容易さの両面から最上位の重大度としています。
High

秘密情報のクライアント露出

FINDING-02(架空例)

概要

外部決済APIのキーが、クライアント側に露出する環境変数(NEXT_PUBLIC_ 接頭辞)で管理されており、ビルド後のJSバンドルに平文で含まれてしまう、という架空の想定です。

該当箇所(架空のサンプルコード)

環境変数定義

// .env(架空のサンプルです)
NEXT_PUBLIC_PAYMENT_API_KEY=sk_live_xxxxxxxxxxxxxxxx

クライアントコンポーネントでの参照

// 架空のサンプルコードです
const key = process.env.NEXT_PUBLIC_PAYMENT_API_KEY;
fetch("https://payment.example.com/charge", {
  headers: { Authorization: `Bearer ${key}` },
});

修正提案(架空のサンプルコード)

// 修正案(架空のサンプルです)
// .env: PAYMENT_API_KEY=sk_live_xxxx (NEXT_PUBLIC_ を外し、サーバー専用にする)

// app/api/charge/route.ts — サーバー側のみでキーを参照
export async function POST(req) {
  const key = process.env.PAYMENT_API_KEY; // サーバー環境変数
  // ここで決済APIを呼び出す。クライアントは /api/charge を叩くのみ
}
重大度の理由(架空例):露出したキーを第三者が直接利用し、決済APIを不正に呼び出せる状態を想定しているため、High(重要)としています。
Medium

入力検証の欠如

FINDING-03(架空例)

概要

部屋検索APIの検索パラメータにバリデーションがなく、不正な値を渡した際のエラーで、内部のスタックトレースがそのままレスポンスに露出してしまう、という架空の想定です。

該当箇所(架空のサンプルコード)

// 架空のサンプルコードです
app.get("/api/units/search", async (req, res) => {
  const rooms = await db.query(
    `SELECT * FROM units WHERE floor = ${req.query.floor}`
  );
  res.json(rooms);
});

修正提案(架空のサンプルコード)

// 修正案(架空のサンプルです)
app.get("/api/units/search", async (req, res) => {
  const parsed = z.coerce.number().int().safeParse(req.query.floor);
  if (!parsed.success) {
    return res.status(400).json({ error: "invalid parameter" });
  }
  try {
    const rooms = await db.unit.findMany({ where: { floor: parsed.data } });
    res.json(rooms);
  } catch (e) {
    logger.error(e); // スタックトレースはログのみに出力し、レスポンスには含めない
    res.status(500).json({ error: "internal error" });
  }
});
重大度の理由(架空例):内部実装の露出に加え、パラメータの組み立て方次第ではSQLインジェクションに繋がりうる書き方であるため、中程度の重大度としています。

残りの指摘(High×1・Medium×2・Low×4)についても、実際の報告書では同一フォーマットで全件記載します。

3付録:レビュー観点チェックリスト

「本サンプルでの扱い」列は、この見本ページ内でどこまで例示したかを示すものです(実際のレビューでは全項目を本文と同水準で記載します)。

#観点本サンプルでの扱い
認証・認可(テナント分離・権限境界・IDOR)確認済み
入力検証・インジェクション確認済み
秘密情報・鍵管理確認済み
個人情報の取り扱い所見のみ
エラー処理・ログ所見のみ
依存パッケージ脆弱性対象外
コード品質・保守性所見のみ
性能・データ量耐性対象外
バックアップ・復旧対象外
リリース運用(環境分離・ロールバック)対象外