Weryfikacja HMAC
Weryfikacja HMAC to sposób, w jaki FastOG potwierdza, że każde żądanie obrazu OG opartego na subskrypcji pochodzi z Twojego własnego serwera — podpisane adresy URL chronią Twoje kredyty. Zarejestruj się, wybierz plan Dynamic API i utwórz klucz API oraz sekret HMAC w panelu (dashboard); renderowanie płatnych API jest czyste. Ta strona obejmuje reguły po stronie serwera dotyczące dynamicznych obrazów Open Graph, od kanonicznego podpisywania zapytań po porównanie w czasie stałym.
Przegląd
Do punktu końcowego /api/v1/og dodano walidację podpisu HMAC-SHA256, aby zapobiec nieautoryzowanemu wykorzystaniu limitu API. Dzięki temu tylko żądania pochodzące z prawidłowej strony internetowej mogą generować obrazy OG.
Architektura
Dwupoziomowe uwierzytelnianie:
- Podpis HMAC (pierwszy) – weryfikuje, że żądanie pochodzi z zaufanego źródła (strony internetowej)
- Klucz API (drugi) – identyfikuje konto użytkownika na potrzeby rozliczania limitu renderowania
Pojedynczy middleware (ApiKeyAuth): weryfikuje podpis (parametry zapytania) i rozpoznaje klucz API do rozliczania limitu renderowania w jednym przebiegu — szybkie odrzucanie żądań z nieprawidłowym podpisem.
Parametry zapytania, nie nagłówki: Ponieważ obrazy OG są pobierane przez roboty sieci społecznościowych (LinkedIn, Twitter, Facebook), które nie wysyłają niestandardowych nagłówków, podpis jest przekazywany w parametrach zapytania URL:
GET /api/v1/og?title=Hello&key=sk_quick_123abc&s=<hmac>
Implementacja
Utworzone/zmodyfikowane pliki
app/Http/Middleware/HmacSignature.php— Nowe middleware walidujące podpisy HMAC (parametry zapytania)routes/api.php— Dodano middleware HMAC do trasy OGbootstrap/app.php— Zarejestrowano alias middlewarehmacapp/Models/ApiKey.php— Dodano polehmac_secret(sekrety per użytkownik)app/Http/Controllers/DashboardController.php— Generuje sekret HMAC przy tworzeniu klucza APIdatabase/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php— Migracja dla kolumny hmac_secretresources/js/Pages/Dashboard/ApiKeys.svelte— Pokazuje sekret HMAC użytkownikowi (jednorazowo)docs/hmac-signature-guide.md— Przewodnik implementacji dla klientatests/Feature/HmacSignatureTest.php— 8 przechodzących testów
Algorytm podpisu
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 jest częścią kanonicznego zapytania, więc podpis jest powiązany z kluczem API. Zmiana dowolnego parametru (tytuł, szablon, obraz itd.) lub samego klucza zmienia zapytanie kanoniczne → niezgodność podpisu → 401 → brak renderowania, brak naliczenia limitu. Identyczne parametry → identyczny podpis → identyczny URL → możliwość cache'owania na brzegu sieci bezterminowo, odtwarzanie bez naliczania.
Funkcje bezpieczeństwa
- Podpis z adresowaniem treści — Zmiana dowolnego parametru → 401 → brak renderowania, brak naliczenia limitu (sam URL jest dowodem)
- Brak znacznika czasu — URL-e są osadzane w publicznym HTML i cache'owane bezterminowo; muszą przetrwać 5 minut
- Porównanie w stałym czasie (
hash_equals()) — Zapobiega atakom typu timing - Klucz API w wiadomości — Podpis jest powiązany z kluczem; podmiana kluczy kończy się niepowodzeniem
- Specyficzny dla ścieżki — Podpis jest ważny tylko dla dokładnej ścieżki
- Specyficzny dla metody — Podpis GET nie zadziała dla POST
- Sekrety per użytkownik — Każdy klucz API ma własny sekret HMAC, który można niezależnie unieważnić (rotacja = wyłącznik awaryjny dla wyciekniętych URL-i)
Konfiguracja
Generowanie sekretu (automatyczne)
Sekret HMAC jest generowany automatycznie podczas tworzenia klucza API w panelu (dashboard). Jest wyświetlany jednorazowo wraz z kluczem API, a następnie nigdy więcej.
Ręczne generowanie (jeśli potrzebne):
openssl rand -hex 32
Schemat bazy danych
ALTER TABLE api_keys ADD COLUMN hmac_secret VARCHAR(64)
COMMENT 'HMAC-SHA256 secret for signing OG image URLs (hex, 64 chars)';
Zmienne środowiskowe
Nie jest potrzebna żadna globalna zmienna środowiskowa — sekrety są przechowywane per użytkownik w tabeli api_keys.
Przykład użycia (backend strony internetowej)
// 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}" />
Odpowiedzi błędów
| Status | Kod | Opis |
|---|---|---|
| 401 | missing_signature |
Brak parametru zapytania key lub s |
| 401 | invalid_signature |
Podpis nie zgadza się z kanonicznym zapytaniem (zmodyfikowane lub błędny sekret) |
Testowanie
Uruchom testy:
php artisan test --filter HmacSignatureTest
Wszystkie 8 testów przechodzi:
- ✅ Żądanie bez podpisu jest odrzucane
- ✅ Żądanie z brakującym parametrem klucza jest odrzucane
- ✅ Zmodyfikowany parametr treści (zmieniony tytuł) jest odrzucany
- ✅ Dodany parametr jest odrzucany
- ✅ Nieprawidłowy podpis jest odrzucany
- ✅ Prawidłowy podpis przechodzi weryfikację HMAC
- ✅ Podpis jest specyficzny dla ścieżki
- ✅ Podpis jest specyficzny dla metody
- ✅ Różne klucze API mają różne sekrety
Wpływ na wydajność
- Pomijalny – obliczenie HMAC-SHA256 zajmuje ~0,1 ms na żądanie
- Brak dodatkowych wywołań sieciowych – walidacja odbywa się lokalnie
- Szybkie odrzucanie – nieprawidłowe podpisy są odrzucane przed wyszukaniem klucza API
Zagadnienia bezpieczeństwa
- Nigdy nie ujawniaj sekretu w kodzie po stronie klienta – podpisuj wyłącznie po stronie serwera
- Używaj protokołu HTTPS – HMAC chroni przed nieautoryzowanymi żądaniami, HTTPS chroni przed atakami MITM
- Okresowo rotuj sekrety – zwłaszcza w przypadku kompromitacji (wymaga ponownego wygenerowania klucza API)
- Monitoruj nieudane podpisy – powtarzające się błędy mogą wskazywać na ataki
- Izolacja per użytkownik – każdy klucz API ma własny sekret HMAC, kompromitacja jednego nie wpływa na pozostałe
Bibliografia
- Przewodnik po podpisach HMAC dla klientów
- Przewodnik szybkiego startu
- OWASP: Ściągawka dotycząca przechowywania kryptograficznego
- PHP: hash_hmac()