Проверка HMAC
Проверка HMAC — это способ, с помощью которого FastOG удостоверяется, что каждый запрос на OG-изображение на основе подписки поступил с вашего собственного сервера: подписанные URL-адреса защищают ваши кредиты. Зарегистрируйтесь, затем выберите тариф Dynamic API и создайте свой API-ключ и HMAC-секрет в панели управления; платные рендеры API выполняются без водяных знаков. Эта страница описывает серверные правила, лежащие в основе динамических изображений Open Graph, — от канонической подписи запроса до сравнения за постоянное время.
Обзор
К конечной точке /api/v1/og добавлена проверка подписи HMAC-SHA256 для предотвращения несанкционированного использования квоты API. Это гарантирует, что только запросы с легитимного веб-сайта могут генерировать OG-изображения.
Архитектура
Двухуровневая аутентификация:
- HMAC-подпись (первый уровень) — проверяет, что запрос исходит из доверенного источника (сайта)
- 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— Зарегистрирован псевдоним промежуточного ПОhmacapp/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_secretresources/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-ключом и больше никогда не показывается.
Ручная генерация (при необходимости):
openssl rand -hex 32
Схема базы данных
ALTER TABLE api_keys ADD COLUMN hmac_secret VARCHAR(64)
COMMENT 'HMAC-SHA256 secret for signing OG image URLs (hex, 64 chars)';
Переменные окружения
Глобальная переменная окружения не требуется — секреты хранятся для каждого пользователя в таблице api_keys.
Пример использования (бэкенд веб-сайта)
// 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 |
Подпись не соответствует каноническому запросу (изменён или неверный секрет) |
Тестирование
Запуск тестов:
php artisan test --filter HmacSignatureTest
Все 8 тестов проходят успешно:
- ✅ Запрос без подписи отклоняется
- ✅ Запрос с отсутствующим параметром ключа отклоняется
- ✅ Изменённый параметр содержимого (изменён заголовок) отклоняется
- ✅ Добавленный параметр отклоняется
- ✅ Недействительная подпись отклоняется
- ✅ Действительная подпись проходит проверку HMAC
- ✅ Подпись привязана к конкретному пути
- ✅ Подпись привязана к конкретному методу
- ✅ Разные ключи API имеют разные секреты
Влияние на производительность
- Незначительное — вычисление HMAC-SHA256 занимает ~0,1 мс на запрос
- Без дополнительных сетевых вызовов — проверка выполняется локально
- Быстрый отказ — недействительные подписи отклоняются до поиска API-ключа
Вопросы безопасности
- Никогда не раскрывайте секрет в клиентском коде — подписывайте только на сервере
- Используйте HTTPS — HMAC защищает от несанкционированных запросов, HTTPS защищает от MITM
- Периодически меняйте секреты — особенно в случае компрометации (требует повторной генерации API-ключа)
- Отслеживайте неудачные подписи — повторные сбои могут указывать на атаки
- Изоляция по пользователям — у каждого API-ключа свой HMAC-секрет, компрометация одного не затрагивает остальные
Ссылки
- Руководство по HMAC-подписи для клиентов
- Краткое руководство по началу работы
- OWASP: Памятка по криптографическому хранению
- PHP: hash_hmac()