Temat

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:

  1. Podpis HMAC (pierwszy) – weryfikuje, że żądanie pochodzi z zaufanego źródła (strony internetowej)
  2. 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 OG
  • bootstrap/app.php — Zarejestrowano alias middleware hmac
  • app/Models/ApiKey.php — Dodano pole hmac_secret (sekrety per użytkownik)
  • app/Http/Controllers/DashboardController.php — Generuje sekret HMAC przy tworzeniu klucza API
  • database/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php — Migracja dla kolumny hmac_secret
  • resources/js/Pages/Dashboard/ApiKeys.svelte — Pokazuje sekret HMAC użytkownikowi (jednorazowo)
  • docs/hmac-signature-guide.md — Przewodnik implementacji dla klienta
  • tests/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):

bash
openssl rand -hex 32

Schemat bazy danych

sql
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)

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}" />

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:

bash
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

  1. Nigdy nie ujawniaj sekretu w kodzie po stronie klienta – podpisuj wyłącznie po stronie serwera
  2. Używaj protokołu HTTPS – HMAC chroni przed nieautoryzowanymi żądaniami, HTTPS chroni przed atakami MITM
  3. Okresowo rotuj sekrety – zwłaszcza w przypadku kompromitacji (wymaga ponownego wygenerowania klucza API)
  4. Monitoruj nieudane podpisy – powtarzające się błędy mogą wskazywać na ataki
  5. Izolacja per użytkownik – każdy klucz API ma własny sekret HMAC, kompromitacja jednego nie wpływa na pozostałe

Bibliografia

Pełna dokumentacja API

Wypróbuj za darmo — bez rejestracji

Podglądaj i testuj dynamiczne obrazy OG w kilka sekund.

Otwórz darmowy tester obrazów OG

A BeCaSimulations site