Тема

Проверка HMAC

Проверка HMAC — это способ, с помощью которого FastOG удостоверяется, что каждый запрос на OG-изображение на основе подписки поступил с вашего собственного сервера: подписанные URL-адреса защищают ваши кредиты. Зарегистрируйтесь, затем выберите тариф Dynamic API и создайте свой API-ключ и HMAC-секрет в панели управления; платные рендеры API выполняются без водяных знаков. Эта страница описывает серверные правила, лежащие в основе динамических изображений Open Graph, — от канонической подписи запроса до сравнения за постоянное время.

Обзор

К конечной точке /api/v1/og добавлена проверка подписи HMAC-SHA256 для предотвращения несанкционированного использования квоты API. Это гарантирует, что только запросы с легитимного веб-сайта могут генерировать OG-изображения.

Архитектура

Двухуровневая аутентификация:

  1. HMAC-подпись (первый уровень) — проверяет, что запрос исходит из доверенного источника (сайта)
  2. API-ключ (второй уровень) — идентифицирует учетную запись пользователя для учета квоты на рендеринг

Единый middleware (ApiKeyAuth): проверяет подпись (параметры запроса) и определяет API-ключ для учета квоты на рендеринг за один проход — при неверной подписи происходит быстрый отказ.

Параметры запроса, а не заголовки: поскольку 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 — Добавлено HMAC-промежуточное ПО в OG-маршрут
  • bootstrap/app.php — Зарегистрирован псевдоним промежуточного ПО hmac
  • app/Models/ApiKey.php — Добавлено поле hmac_secret (секреты для каждого пользователя)
  • app/Http/Controllers/DashboardController.php — Генерация HMAC-секрета при создании API-ключа
  • database/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php — Миграция для колонки hmac_secret
  • 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 является частью канонического запроса, поэтому подпись привязана к API-ключу. Изменение любого параметра (title, template, image и т. д.) или самого ключа меняет канонический запрос → несовпадение подписи → 401 → без рендеринга, без учёта квоты. Идентичные параметры → идентичная подпись → идентичный URL → кэшируется на периферии бессрочно, воспроизводится без учёта квоты.

Функции безопасности

  • Контентно-адресуемая подпись — Любое изменение параметра → 401 → без рендеринга, без учёта квоты (сам URL является доказательством)
  • Без временной метки — URL встраиваются в публичный HTML и кэшируются бессрочно; они должны переживать 5 минут
  • Сравнение за постоянное время (hash_equals()) — Предотвращает атаки по времени
  • API-ключ в сообщении — Подпись привязана к ключу; замена ключей приводит к ошибке
  • Привязка к пути — Подпись действительна только для точного пути
  • Привязка к методу — Подпись GET не будет работать для POST
  • Секреты для каждого пользователя — Каждый API-ключ имеет собственный HMAC-секрет, который можно отозвать независимо (ротация = аварийное отключение утёкших URL)

Конфигурация

Генерация секрета (автоматически)

Секрет HMAC автоматически генерируется при создании API-ключа в панели управления (dashboard). Он отображается один раз вместе с 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 тестов проходят успешно:

  • ✅ Запрос без подписи отклоняется
  • ✅ Запрос с отсутствующим параметром ключа отклоняется
  • ✅ Изменённый параметр содержимого (изменён заголовок) отклоняется
  • ✅ Добавленный параметр отклоняется
  • ✅ Недействительная подпись отклоняется
  • ✅ Действительная подпись проходит проверку 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