← 返回範例總覽
困難 35 mins

實戰範例 08:API 速率限制與安全防護

為 API 路由實作速率限制 (Rate Limiting) 防護,阻擋惡意暴力破解或 DDoS 攻擊。

使用指令 /opsx:propose/opsx:apply/opsx:archive
#安全性更新#速率限制#Redis#防護機制

實戰背景

開放的 API 介面如果缺乏防護機制,極易受到惡意爬蟲、暴力密碼破解 (Brute-force Attacks) 或阻斷服務攻擊 (DDoS)。為了保障系統的可用性與資訊安全,我們必須對高敏感 API 路由(例如:登入、註冊、發送驗證碼)實作速率限制 (Rate Limiting) 機制。

本範例中,我們將透過整合 Redis 與令牌桶演算法 (Token Bucket Algorithm),為 API 路由加上「單一 IP 每分鐘最多 60 次請求」的防護。我們將使用 OpenSpec 的 快速開發工作流 (Quick Flow) 來進行此項任務。


規格定義

本次變更計畫的 Capability 為 security-hardening。我們預計在規格增量 (Spec Delta) 中定義以下驗收標準:

## ADDED Requirements

### Requirement: API rate limiting
The system SHALL apply rate limits to all public endpoints to prevent abuse.

#### Scenario: Rate limit exceeded response
- **WHEN** an IP address exceeds 60 requests per minute to any API endpoint
- **THEN** the system SHALL reject subsequent requests with a 429 Too Many Requests status and return Retry-After header

端到端 6 輪 AI 協作紀錄

以下是 You 與 AI 助手的完整對話歷史。

── 第 1 輪:建立計畫與提案 ──

  • You:
    /opsx:propose "為全站 API 加上 Rate Limiting 限制,單一 IP 每分鐘限 60 次,超限回傳 429 與 Retry-After"
    
  • AI:

    好的,已建立變更計畫 add-api-rate-limiting

    • 建立 proposal.md: 說明限制機制、超限時的 429 狀態碼與 Retry-After 標頭。
    • 建立 specs/security-hardening/spec.md: 新增超限響應的驗收場景 (Scenario)。
    • 建立 tasks.md: 包含以下 4 個實作任務:
      • 1.1 安裝並配置 Redis 連線服務
      • 1.2 實作以 IP 為鍵值的速率計數器與 TTL 邏輯
      • 1.3 建立 API 中間件 (Middleware) 進行超限阻擋
      • 1.4 撰寫高頻請求測試案例驗證 429

    請執行 /opsx:apply 開始實作。


── 第 2 輪:審查與微調提案 ──

  • You:
    如果 Redis 斷線,不應該讓整站 API 掛掉,要有 Fail-Safe (安全降級) 機制,允許請求通過,並記錄 Error log。
    
  • AI:

    好的,已使用 /opsx:update 更新文件:

    • 更新 design.md:設計 Fail-Safe 邏輯,當捕獲 Redis 連線錯誤時,記錄警告日誌 (Warn Log),並直接讓 API 請求通過 (Bypass)。
    • 更新 tasks.md:在任務 1.3 中加入「Redis 斷線 Fail-safe 測試」的子任務。

    修改已保持一致。現在您可以執行 /opsx:apply 了。


── 第 3 輪:實作任務 1 (Redis 連線配置) ──

  • You:
    /opsx:apply
    
  • AI:

    Implementing: add-api-rate-limiting (schema: spec-driven)

    正在處理任務 1/4:安裝並配置 Redis 連線服務。

    我已在專案中安裝 ioredis 並於 src/utils/redisClient.ts 中配置完成:

    import Redis from 'ioredis';
    
    export const redis = new Redis(process.env.REDIS_URL || 'redis://localhost:6379', {
      maxRetriesPerRequest: 1, // 快速失敗以配合 Fail-safe
    });
    

    ✓ 任務已完成。已將 tasks.md 中的任務 1.1 標記為完成。

    準備好進入任務 1.2「實作計數器邏輯」了嗎?


── 第 4 輪:實作任務 2 (速率計數器邏輯) ──

  • You:
    繼續實作計數器。
    
  • AI:

    Implementing: add-api-rate-limiting

    正在處理任務 2/4:實作以 IP 為鍵值的速率計數器與 TTL 邏輯。

    我已在 src/utils/rateLimiter.ts 實作了利用 Redis MULTI 指令進行原子性操作(增量與設定過期時間)的邏輯:

    import { redis } from './redisClient';
    
    export async function isRateLimited(ip: string, limit = 60): Promise<{ limited: boolean; reset: number }> {
      const key = `ratelimit:${ip}`;
      try {
        const [count] = await redis.multi().incr(key).ttl(key).exec() as any;
        const currentCount = count[1];
        if (currentCount === 1) {
          await redis.expire(key, 60); // 首次請求,設定 TTL 60秒
        }
        return { limited: currentCount > limit, reset: 60 };
      } catch (err) {
        console.error('Redis 速率限制防護異常:', err);
        return { limited: false, reset: 0 }; // Fail-safe 降級直接放行
      }
    }
    

    ✓ 任務已完成。已將任務 1.2 標記為完成。

    下一步將實作 API 路由中間件。是否繼續?


── 第 5 輪:實作任務 3 (中間件超限阻擋) ──

  • You:
    繼續實作中間件。
    
  • AI:

    Implementing: add-api-rate-limiting

    正在處理任務 3/4:建立 API 中間件 (Middleware) 進行超限阻擋。

    我已在 src/middleware.ts 中攔截 API 請求並套用防護邏輯:

    import { isRateLimited } from './utils/rateLimiter';
    
    export async function onRequest({ request, next }) {
      const url = new URL(request.url);
      if (url.pathname.startsWith('/api/')) {
        const ip = request.headers.get('x-forwarded-for') || '127.0.0.1';
        const { limited, reset } = await isRateLimited(ip);
        
        if (limited) {
          return new Response(JSON.stringify({ error: '請求過於頻繁' }), {
            status: 429,
            headers: {
              'Content-Type': 'application/json',
              'Retry-After': String(reset),
            },
          });
        }
      }
      return next();
    }
    

    ✓ 任務已完成。已將任務 1.3 標記為完成。

    剩下最後一個測試任務。是否繼續?


── 第 6 輪:撰寫測試、驗證與歸檔 ──

  • You:
    執行測試,verify 並歸檔。
    
  • AI:

    Implementing: add-api-rate-limiting

    正在處理任務 4/4:撰寫高頻請求測試案例。

    我已在 tests/rateLimit.test.ts 中實作測試。

    • 連續發送 61 次請求,第 61 次成功被攔截並返回 429 狀態碼。
    • 測試了當 Redis 被斷開連線時,Fail-safe 邏輯能直接放行請求。

    ✓ 4/4 任務全數完成。

    自動驗證與歸檔:

    1. 驗證成功:實作完全符合 Scenario: Rate limit exceeded response 的要求。
    2. 智慧同步:已將暫存規格合併至主規格 openspec/specs/security-hardening/spec.md
    3. 目錄歸檔:已將計畫資料夾移至 openspec/changes/archive/2026-07-14-add-api-rate-limiting/

    Archive Complete

    變更計畫已成功歸檔!