主題

HMAC 驗證

HMAC 驗證是 FastOG 用來確認每個基於訂閱的 OG 圖片請求皆來自您自己伺服器的方式——簽署的 URL 能保護您的額度安全。請先註冊,然後選擇 Dynamic API 方案,並在儀表板中建立您的 API 金鑰與 HMAC 密鑰;付費 API 的渲染結果是乾淨的。本頁涵蓋動態 Open Graph 圖片背後的伺服器端規則,從標準查詢簽署到常數時間比較。

概述

/api/v1/og 端點已加入 HMAC-SHA256 簽章驗證,以防止 API 配額遭到未授權使用。此機制可確保只有來自合法網站的請求才能產生 OG 圖片。

架構

兩層驗證:

  1. HMAC 簽章(第一層)— 驗證請求確實來自受信任的來源(網站本身)
  2. API 金鑰(第二層)— 識別使用者帳號,用於渲染配額(render-quota)計量

單一中間件(ApiKeyAuth): 在一次處理中同時驗證簽章(查詢參數)並解析 API 金鑰以進行渲染配額計量 — 遇到無效簽章時立即失敗(fail fast)。

使用查詢參數,而非標頭: 由於 OG 圖片是由社群網路爬蟲(LinkedIn、Twitter、Facebook)擷取的,而這些爬蟲不會傳送自訂標頭,因此簽章是透過 URL 查詢參數傳遞:

GET /api/v1/og?title=Hello&key=sk_quick_123abc&s=<hmac>

實作

建立/修改的檔案

  • app/Http/Middleware/HmacSignature.php - 新的中介層(middleware),驗證 HMAC 簽章(查詢參數)
  • routes/api.php - 在 OG 路由中加入 HMAC 中介層
  • bootstrap/app.php - 註冊 hmac 中介層別名
  • app/Models/ApiKey.php - 新增 hmac_secret 欄位(每個使用者各自的密鑰)
  • app/Http/Controllers/DashboardController.php - 在建立 API 金鑰時產生 HMAC 密鑰
  • database/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php - 用於 hmac_secret 欄位的遷移(migration)
  • resources/js/Pages/Dashboard/ApiKeys.svelte - 向使用者顯示 HMAC 密鑰(僅一次)
  • docs/hmac-signature-guide.md - 用戶端實作指南
  • tests/Feature/HmacSignatureTest.php - 8 個通過的測試

簽章演算法

method = HTTP method (e.g., "GET")
path  = request path (e.g., "/api/v1/og")
query = the request's DECODED query params

canonical = every param except `s`, keys sorted (array values sorted too),
            each `key=value` RFC-3986 encoded (unreserved-only), joined with "&"
message   = method + "/" + path + "\n" + canonical
signature = HMAC-SHA256(message, hmac_secret)

Query parameters:
  key=<apiKey>
  s=<signature>
  (no timestamp)

key 是正規查詢(canonical query)的一部分,因此簽章與 API 金鑰綁定。變更任何參數(標題、範本、圖片等)或金鑰本身,都會改變正規查詢 → 簽章不符 → 401 → 不渲染、不計配額。參數相同 → 簽章相同 → URL 相同 → 可在邊緣快取(edge)永久儲存,重播時不計次數。

安全功能

  • 內容定址簽章 - 任何參數變更 → 401 → 不渲染、不計配額(URL 本身就是證明)
  • 無時間戳 - URL 嵌入公開 HTML 並永久快取;必須能存活超過 5 分鐘
  • 常數時間比較(hash_equals()) - 防止計時攻擊(timing attack)
  • 訊息中包含 API 金鑰 - 簽章與金鑰綁定;更換金鑰即失效
  • 路徑限定 - 簽章僅對確切路徑有效
  • 方法限定 - GET 簽章無法用於 POST
  • 每個使用者各自的密鑰 - 每個 API 金鑰有獨立的 HMAC 密鑰,可獨立撤銷(輪換=已外洩 URL 的緊急停止開關)

設定

產生密鑰(自動)

在儀表板中建立 API 金鑰時,系統會自動產生 HMAC 密鑰。該密鑰會與 API 金鑰一同顯示一次,之後便不再顯示。

手動產生(如有需要):

bash
openssl rand -hex 32

資料庫結構

sql
ALTER TABLE api_keys ADD COLUMN hmac_secret VARCHAR(64)
  COMMENT 'HMAC-SHA256 secret for signing OG image URLs (hex, 64 chars)';

環境變數

不需要全域環境變數——密鑰會以每個使用者為單位儲存在 api_keys 資料表中。

使用範例(網站後端)

javascript
// Node.js example - generate signed OG image URL
const crypto = require("crypto");

// RFC-3986 encode (encodeURIComponent leaves !'()* unencoded).
const enc = (s) =>
    encodeURIComponent(s).replace(
        /[!'()*]/g,
        (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(),
    );

function generateOgImageUrl(baseUrl, params, apiKey, hmacSecret) {
    const all = { ...params, key: apiKey };

    // Canonical: every param (except s), sorted, RFC-3986 encoded, joined with "&".
    const pairs = [];
    for (const [k, v] of Object.entries(all)) {
        for (const vv of Array.isArray(v) ? [...v].sort() : [v])
            pairs.push(`${enc(k)}=${enc(vv)}`);
    }
    pairs.sort();
    const canonical = pairs.join("&");

    const message = `GET/api/v1/og\n${canonical}`;
    const signature = crypto
        .createHmac("sha256", hmacSecret)
        .update(message)
        .digest("hex");

    const url = new URL(baseUrl);
    for (const [k, v] of Object.entries(all)) {
        const isArray = Array.isArray(v);
        for (const vv of isArray ? v : [v])
            url.searchParams.append(isArray ? `${k}[]` : k, vv);
    }
    url.searchParams.set("s", signature);
    return url.toString();
}

// Usage in meta tag
const ogUrl = generateOgImageUrl(
    "https://fastog.com/api/v1/og",
    { title: "Como criar um SaaS", template: "blog" },
    "sk_quick_123abc",
    process.env.HMAC_SECRET,
);

// <meta property="og:image" content="{ogUrl}" />

錯誤回應

狀態 代碼 說明
401 missing_signature 缺少 key 或 s 查詢參數
401 invalid_signature 簽章與規範查詢不符(可能遭竄改或密鑰錯誤)

測試

執行測試:

bash
php artisan test --filter HmacSignatureTest

全部 8 項測試皆通過:

  • ✅ 沒有簽章的請求會被拒絕
  • ✅ 缺少 key 參數的請求會被拒絕
  • ✅ 內容參數遭竄改(title 被更改)會被拒絕
  • ✅ 新增參數會被拒絕
  • ✅ 無效簽章會被拒絕
  • ✅ 有效簽章通過 HMAC 檢查
  • ✅ 簽章與路徑相關
  • ✅ 簽章與方法相關
  • ✅ 不同的 API 金鑰有不同的密鑰

效能影響

  • 可忽略不計 — HMAC-SHA256 計算每次請求約 0.1 毫秒
  • 無額外網路呼叫 — 驗證在本地端進行
  • 快速失敗 — 無效簽章會在 API 金鑰查詢前即被拒絕

安全考量

  1. 切勿在客戶端程式碼中暴露密鑰 — 僅在伺服器端簽章
  2. 使用 HTTPS — HMAC 防止未授權請求,HTTPS 防止中間人攻擊(MITM)
  3. 定期輪換密鑰 — 特別是在密鑰可能遭洩漏時(需重新產生 API 金鑰)
  4. 監控簽章失敗 — 重複失敗可能表示遭受攻擊
  5. 依使用者隔離 — 每個 API 金鑰擁有各自的 HMAC 密鑰,單一密鑰遭洩漏不會影響其他使用者

參考資料

完整 API 參考文件

免費試用 — 無需註冊

在幾秒內預覽和測試動態 OG 圖片。

開啟免費 OG 圖片測試器

A BeCaSimulations site