Dev Study
← 解説「CORS と Helmet」に戻る

サンプルコードで身につける: CORS と Helmet

解説で学んだ概念を、5つの具体例で確認します。素朴な例から実務寄りの例へと進みます。

1同一オリジンポリシーとは

ブラウザが課す同一オリジンポリシーを図で示す例です。別オリジンの API 呼び出しが既定でブロックされる、という制約が CORS 設定の出発点になります。

// オリジン = スキーム + ホスト + ポート の組
//   https://app.example.com:443
//   └─ scheme ─┘└─── host ────┘└port┘
//
// ブラウザの同一オリジンポリシー: 別オリジンへの呼び出しは既定で制限
//
//   https://app.example.com のページから
//     → https://app.example.com/users  … 同一オリジン: OK
//     → https://api.example.com/users  … 別オリジン: 既定でブロック
//
// フロントと API のドメインが違う構成では、
// API 側が「このオリジンは許可する」と明示する必要がある = CORS

2enableCors の最小設定

main.ts で app.enableCors() を呼んで特定オリジンを許可する最小例です。これ1行で、指定したフロントエンドからのブラウザ経由のアクセスが通るようになります。

import { NestFactory } from "@nestjs/core";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // このオリジンからのブラウザ経由アクセスを許可する
  app.enableCors({ origin: "https://app.example.com" });

  await app.listen(3000);
}
bootstrap();

3CORSはブラウザにしか効かない

CORS が「ブラウザが課す制約」であって本当のアクセス制御ではないことを、プレーン TypeScript の判定で再現した例です。curl やサーバー間通信はオリジンを送らないため、CORS では止められないことが分かります。

TypeScript

4Helmet でセキュリティヘッダを付与する

helmet を適用してセキュリティ関連の HTTP ヘッダをまとめて付ける例です。クリックジャッキングや MIME スニッフィングを防ぐヘッダが自動で付くため、本番公開前の定番対策になります。

import { NestFactory } from "@nestjs/core";
import helmet from "helmet";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // セキュリティ関連の HTTP ヘッダをまとめて付与する
  // 例: X-Frame-Options(クリックジャッキング対策)
  //     X-Content-Type-Options(MIME スニッフィング対策)
  app.use(helmet());

  await app.listen(3000);
}
bootstrap();

5本番のオリジン許可リスト

環境変数で渡した複数の許可オリジンをリストで照合する、実務の本番向け CORS 設定です。origin にコールバックを渡すと、許可リストにあるオリジンだけを動的に通せます。

import { NestFactory } from "@nestjs/core";
import helmet from "helmet";
import { AppModule } from "./app.module";

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  // 本番では許可オリジンを環境変数のリストで管理する
  // 例: "https://app.example.com,https://admin.example.com"
  const allowlist = (process.env.CORS_ORIGINS ?? "").split(",");

  app.enableCors({
    // origin を関数にすると許可可否を動的に判定できる
    origin: (origin, callback) => {
      // origin が無い(サーバー間)か、許可リストに含まれれば通す
      if (!origin || allowlist.includes(origin)) {
        callback(null, true);
      } else {
        callback(new Error("このオリジンは許可されていません"));
      }
    },
    credentials: true, // Cookie 付きリクエストを許可
  });

  app.use(helmet());
  await app.listen(3000);
}
bootstrap();