C
セキュリティ/APIセキュリティ/Lesson 04

入力バリデーション + API セキュリティ — Rate Limiting · CORS · 環境変数

45分·theory

入力バリデーション + API セキュリティ — Rate Limiting · CORS · 環境変数

🎯 このレッスンを読み終えたら

このレッスンを読み終えると、以下の 3つを自信を持ってできるようになります。

  • ✅ .env の管理 + GitHub Secret Scanning
  • ✅ API Rate Limit (Token Bucket) の実装
  • ✅ CORS ホワイトリスト + helmet セキュリティヘッダー

学習目標をチェックリストとして置き、全て答えられるようになったらレッスンを閉じてください。

.env ファイルの管理 — *GitHub leak 事故の原因第 1位*

機密情報をコードに直接埋め込まないでください

javascript
// ❌ 絶対禁止
const dbPassword = 'mysecret123';
const awsKey = 'AKIA...';

Git にプッシュすると永久記録になります。誰かに見つかると即座に悪用されます。

.env ファイルで分離する

bash
# .env
DATABASE_URL=postgresql://user:pass@localhost/db
JWT_SECRET=super_secret_random_string
AWS_ACCESS_KEY=AKIA...
AWS_SECRET_KEY=...
OPENAI_API_KEY=sk-...
javascript
// コードから参照
const db = process.env.DATABASE_URL;
const secret = process.env.JWT_SECRET;

.gitignore に必ず追加

code
# .gitignore
.env
.env.local
.env.production
*.pem
*.key

プロジェクトを作成したらすぐに追加してください。一度コミットされると、git filter-repo のようなツールで履歴を全部書き直す大規模作業が必要になります。

.env.example は OK

bash
# .env.example — 実際の値なし、キー名だけ
DATABASE_URL=
JWT_SECRET=
AWS_ACCESS_KEY=

他の開発者に「これらのキーが必要です」と伝えるためのガイド。実際の値は含まれないためコミット OK

GitHub leak 事故 — 実際の事例

  • AWS キー漏洩 → 24時間以内に自動ボットが発見 → ビットコインマイニング → 高額請求
  • Stripe キー漏洩 → 決済システムの乗っ取り
  • OpenAI キー漏洩 → トークンの無限消費

GitHub Secret Scanning が自動検出しますが、お金はすでに消えた後です。

キーが漏洩した場合

1. 即座にキーを失効させる (rotate) — AWS/OpenAI コンソールで新しいキーを発行し、古いキーを削除
2. Git の履歴を整理git filter-repo または BFG Repo-Cleaner
3. 請求を監視 — 請求書を確認
4. チームに通知

「一度漏洩 = 永久漏洩」 — 新しいキーの発行は必須です。

環境ごとに分離

code
.env.local           # ローカル開発
.env.development     # 開発サーバー
.env.production      # 本番サーバー

本番環境の値は GitHub Actions Secrets、AWS Parameter Store、Vault のような専用ストアに保管してください。絶対に .env ファイルでサーバーに置かないでください(バックアップ・ログ・ミスによる漏洩リスクがあります)。

🤖 AI にこう依頼してみてください

  • 「このコードのハードコードされた API キーを process.env に移動して、.env.example も作成して」
  • 「このプロジェクトの .gitignore が安全かレビューして」

CORS · Rate Limiting · HTTPS — 3つの防衛ライン

CORS — なぜブラウザがブロックするのか

CORS (Cross-Origin Resource Sharing) はブラウザの安全機能です。別のドメインへの API 呼び出しをデフォルトでブロックします。

code
フロントエンド: https://app.example.com
API:           https://api.other.com
→ ブラウザがブロック (CORS error)

なければ — 悪意あるサイトが私の銀行サイトの API を私のログインクッキーで呼び出すことができます。

サーバーで許可する

Express (Node.js)

javascript
import cors from 'cors';

app.use(cors({
    origin: ['https://app.example.com'],   // 許可する origin を明示
    credentials: true,                       // クッキー付きリクエストを許可
    methods: ['GET', 'POST', 'PUT', 'DELETE']
}));

Spring Boot

java
@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/api/**")
            .allowedOrigins("https://app.example.com")
            .allowedMethods("*")
            .allowCredentials(true);
    }
}

アンチパターン — 全 origin を許可する

javascript
app.use(cors({ origin: '*' }));   // ❌ セキュリティ最悪

公開 API でなければ、ホワイトリストで origin を明示してください。

Rate Limiting — 乱用防止

なぜ必要か

  • 攻撃者が 1秒に 1万回呼び出す → サーバーダウン
  • パスワードのブルートフォース攻撃
  • AI API コストの爆発

Express + express-rate-limit

javascript
import rateLimit from 'express-rate-limit';

const limiter = rateLimit({
    windowMs: 15 * 60 * 1000,    // 15分
    max: 100,                     // IP あたり 100回
    message: 'リクエストが多すぎます'
});

app.use('/api/', limiter);

Redis ベース(分散環境)

javascript
import { rateLimit } from 'express-rate-limit';
import RedisStore from 'rate-limit-redis';

const limiter = rateLimit({
    store: new RedisStore({ /* ... */ }),
    windowMs: 60_000,
    max: 10
});

サーバーインスタンスが複数台の場合は Redis で共有します。単一サーバーのメモリカウントは分散環境には不向きです。

ログインエンドポイントにはより厳格に

javascript
const loginLimiter = rateLimit({
    windowMs: 15 * 60 * 1000,
    max: 5,           // 15分で 5回
    skipSuccessfulRequests: true   // 成功したリクエストはカウントしない
});

app.post('/auth/login', loginLimiter, loginHandler);

HTTPS — 2026年における必須要件

なぜ必須なのか

  • HTTP は平文送信 — Wi-Fi で盗聴される可能性がある
  • Chrome / Safari が「安全でない」と表示 → 信頼性が失われる
  • Service Worker・カメラなどのモダン API は HTTPS を強制

Let's Encrypt 無料証明書

bash
# Nginx + Certbot
sudo certbot --nginx -d example.com -d www.example.com

有効期限 90日 + 自動更新。0円で全てのサイトが HTTPS に。

Vercel · Netlify

自動的に HTTPS が適用されます。追加設定は不要。

セキュリティヘッダー 5つ — 無料のポイント

javascript
// Express の helmet ミドルウェア 1行で完了
import helmet from 'helmet';
app.use(helmet());

helmet が自動設定するもの:

  • X-Content-Type-Options: nosniff
  • X-Frame-Options: SAMEORIGIN (クリックジャッキング対策)
  • Strict-Transport-Security (HSTS — HTTPS を強制)
  • X-XSS-Protection (旧ブラウザ向け)
  • Referrer-Policy: strict-origin-when-cross-origin

🤖 AI にこう依頼してみてください

  • 「この Express API に helmet + CORS ホワイトリスト + グローバル rate limit を追加して」
  • 「ログインエンドポイントに 5分で 5回の制限を追加して」
  • 「このコードのセキュリティ脆弱性を 5つ見つけて」
次のおすすめ: DevOps
入力バリデーション + API セキュリティ — Rate Limiting · CORS · 環境変数 - セキュリティ