Konu

HMAC İmza Kılavuzu

Bu HMAC imza kılavuzu, FastOG için HMAC-SHA256 imzaları oluşturmanızı adım adım anlatır; böylece anında dinamik Open Graph görselleri üretebilirsiniz — abonelik tabanlı, ücretsiz hesap ve Dinamik API planıyla (ücretli işlemler temizdir). İmzayı burada herhangi bir dilde elle oluşturabilir veya URL biçimini önce görmek için Ücretsiz OG Görsel Test Aracı'nı kullanabilirsiniz.

Genel Bakış

/api/v1/og adresine yapılan tüm istekler, meşru web sitesinden geldiklerini kanıtlamak için bir HMAC-SHA256 imzası içermelidir. Bu, yetkisiz tarafların API kotanızı kullanmasını önler.

İki katmanlı kimlik doğrulama:

  1. API Anahtarı (sorguda: key=sk_quick_123abc) — kullanıcı hesabını tanımlar (işleme kotası muhasebesi için)
  2. HMAC İmzası (sorguda: s=<signature>) — isteğin web sitenizden geldiğini kanıtlar

Her ikisi de geçmelidir. Yalnızca API anahtarı YETERLİ DEĞİLDİR.

Neden sorgu parametreleri? OG görselleri, özel başlıklar göndermeyen sosyal ağ tarayıcıları (LinkedIn, Twitter, Facebook) tarafından getirilir. İmza URL'de OLMALIDIR.

İmzanın Oluşturulması

Algoritma

method = "GET"
path = "/api/v1/og"
canonicalQuery = for every query param EXCEPT `s` (including `key`):
  - array values are sorted too
  - each pair: rfc3986Encode(key)=rfc3986Encode(value)
  - pairs sorted lexicographically and joined with "&"

message = method + path + "\n" + canonicalQuery
signature = hex(HMAC-SHA256(message, hmac_secret))

Query parameters:
  key=<apiKey>
  s=<signature>

Gereksinimler

  • İmza formatı: 64 karakterlik onaltılık (hex) dize (SHA256)
  • hash_equals() kullanın sabit zamanlı karşılaştırma için (zamanlama saldırılarını önler)
  • Kullanıcı başına gizli anahtarlar: Her API anahtarının kendi hmac_secret alanı vardır

Örnek Uygulamalar

Birden fazla framework ve desen içeren kapsamlı eğitimler için bkz.:

JavaScript (Node.js / Frontend)

javascript
const crypto = require("crypto");

// rfc3986-encode: everything except A-Za-z0-9-._~
const enc = (s) =>
    encodeURIComponent(s).replace(
        /[!'()*]/g,
        (c) => "%" + c.charCodeAt(0).toString(16).toUpperCase(),
    );

function generateOgImageUrl(baseUrl, params, apiKey, hmacSecret) {
    // every query param except `s`; key included; arrays expand to one pair each
    const all = { ...params, key: apiKey };
    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 signature = crypto
        .createHmac("sha256", hmacSecret)
        .update(`GET/api/v1/og\n${canonical}`)
        .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: "default" },
    "sk_quick_123abc",
    process.env.HMAC_SECRET,
);

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

PHP (Laravel)

php
function generateOgImageUrl(string $baseUrl, array $params, string $apiKey, string $hmacSecret): string
{
    // every query param except `s`; key included; array values sorted too
    $all = array_merge($params, ['key' => $apiKey]);

    $pairs = [];
    foreach ($all as $k => $v) {
        $values = is_array($v) ? $v : [$v];
        sort($values, SORT_STRING);
        foreach ($values as $vv) {
            $pairs[] = rawurlencode((string)$k) . '=' . rawurlencode((string)$vv);
        }
    }
    sort($pairs, SORT_STRING);
    $canonical = implode('&', $pairs);

    $signature = hash_hmac('sha256', 'GET/api/v1/og' . "\n" . $canonical, $hmacSecret);

    // URL uses bracket keys for arrays (features[]=a&features[]=b); canonical signs plain keys
    $urlPairs = [];
    foreach ($all as $k => $v) {
        $isArray = is_array($v);
        $values = $isArray ? $v : [$v];
        $urlKey = $isArray ? $k . '[]' : $k;
        foreach ($values as $vv) {
            $urlPairs[] = rawurlencode($urlKey) . '=' . rawurlencode((string)$vv);
        }
    }

    return $baseUrl . '?' . implode('&', $urlPairs) . '&s=' . $signature;
}

// Usage in Blade template:
$ogUrl = generateOgImageUrl(
    'https://fastog.com/api/v1/og',
    ['title' => 'Como criar um SaaS'],
    'sk_quick_123abc',
    env('HMAC_SECRET')
);

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

Python

python
import hmac
import hashlib
from urllib.parse import quote

def _enc(s):
    return quote(str(s), safe='-._~')

def generate_og_image_url(base_url, params, api_key, hmac_secret):
    # every query param except `s`; key included; array values sorted too
    all_params = {**params, 'key': api_key}

    pairs = []
    for k, v in all_params.items():
        for vv in sorted(v if isinstance(v, list) else [v]):
            pairs.append(f"{_enc(k)}={_enc(vv)}")
    canonical = '&'.join(sorted(pairs))

    signature = hmac.new(
        hmac_secret.encode(),
        f"GET/api/v1/og\n{canonical}".encode(),
        hashlib.sha256
    ).hexdigest()

    # URL uses bracket keys for arrays (features[]=a&features[]=b); canonical signs plain keys
    url_pairs = []
    for k, v in all_params.items():
        if isinstance(v, list):
            for vv in v:
                url_pairs.append(f"{_enc(f'{k}[]')}={_enc(vv)}")
        else:
            url_pairs.append(f"{_enc(k)}={_enc(v)}")

    return f"{base_url}?{'&'.join(url_pairs)}&s={signature}"

# Usage:
og_url = generate_og_image_url(
    'https://fastog.com/api/v1/og',
    {'title': 'Como criar um SaaS', 'template': 'default'},
    'sk_quick_123abc',
    os.environ['HMAC_SECRET']
)

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

Tam Örnek: Laravel Blade Şablonu

blade
{{-- In your blog post view --}}
@php
    $apiKey = 'sk_quick_123abc'; // Your full API key
    $hmacSecret = env('OG_RENDER_HMAC_SECRET');

    $params = [
        'template' => 'blog',
        'title' => $post->title,
        'subtitle' => $post->excerpt,
        'key' => $apiKey,
    ];

    // canonical query: sorted "k=v" pairs, rfc3986-encoded, every param except `s`
    $pairs = [];
    foreach ($params as $k => $v) {
        $pairs[] = rawurlencode((string)$k) . '=' . rawurlencode((string)$v);
    }
    sort($pairs, SORT_STRING);
    $canonical = implode('&', $pairs);
    $signature = hash_hmac('sha256', 'GET/api/v1/og' . "\n" . $canonical, $hmacSecret);

    $ogImageUrl = 'https://fastog.com/api/v1/og?' . $canonical . '&s=' . $signature;
@endphp

<head>
    <meta property="og:image" content="{{ $ogImageUrl }}" />
    <meta property="og:title" content="{{ $post->title }}" />
    <meta property="og:description" content="{{ $post->excerpt }}" />
</head>

Güvenlik En İyi Uygulamaları

  1. Sırları güvenli şekilde saklayın - Ortam değişkenleri veya bir sır yöneticisi kullanın
  2. Sırları asla istemci tarafı kodda açığa çıkarmayın - URL'leri yalnızca sunucuda imzalayın
  3. HTTPS kullanın - MITM saldırılarını önler
  4. Sırları periyodik olarak döndürün - Özellikle tehlikeye girildiyse
  5. Başarısız imzaları izleyin - Tekrarlanan başarısızlıklar saldırı göstergesi olabilir
  6. Kullanıcı başına izolasyon - Her API anahtarının kendi HMAC sırrı vardır

Hata Yanıtları

Eksik İmza

json
{
    "error": "Missing signature parameters",
    "code": "missing_signature",
    "required_params": ["s", "key"],
    "example": "/api/v1/og?title=Hello&key=sk_quick_123abc&s=<signature>"
}

Geçersiz İmza

json
{
    "error": "Invalid signature",
    "code": "invalid_signature"
}

Test Etme

Bir test URL’si oluşturun:

bash
# Node.js
node -e "
const crypto = require('crypto');
const enc = (s) => encodeURIComponent(s).replace(/[!'()*]/g, (c) => '%' + c.charCodeAt(0).toString(16).toUpperCase());
const all = { title: 'Test', key: 'sk_quick_123abc' };
const pairs = [];
for (const [k, v] of Object.entries(all)) for (const vv of Array.isArray(v) ? v : [v]) pairs.push(enc(k) + '=' + enc(vv));
pairs.sort();
const canonical = pairs.join('&');
const sig = crypto.createHmac('sha256', 'YOUR_HMAC_SECRET').update('GET/api/v1/og\n' + canonical).digest('hex');
console.log('https://fastog.com/api/v1/og?title=Test&key=sk_quick_123abc&s=' + sig);
"

# Test with curl
curl "https://fastog.com/api/v1/og?title=Test&key=sk_quick_123abc&s=<signature>"

Beklenen yanıtlar:

  • ✅ Geçerli imza → OG görselini döndürür (şablon parametreleri eksikse doğrulama hatası döner)
  • ❌ Geçersiz imza → {"error": "...", "code": "invalid_signature"}
  • ❌ Eksik parametreler → {"error": "...", "code": "missing_signature"}

Sorun Giderme

İmza eşleşmiyor

  1. Doğru HMAC gizli anahtarını kullandığınızı doğrulayın (paneldeki API anahtarını kontrol edin)
  2. Kanonik sorguyu kontrol edin: s hariç tüm sorgu parametreleri (key dahil), değerler rfc3986 kodlu, anahtarlar sözlükbilimsel olarak sıralanmış
  3. İmzanın küçük harfli onaltılık (64 karakter) olduğunu doğrulayın

HMAC Sırrınızı Alma

  1. Kontrol Paneli → API Anahtarları bölümüne gidin
  2. Yeni bir API anahtarı oluşturun (veya mevcut birini kullanın)
  3. API anahtarının yanında gösterilen HMAC Sırrını kopyalayın (yalnızca bir kez gösterilir!)
  4. Bunu ortam değişkenlerinizde güvenli bir şekilde saklayın
bash
# Add to .env
OG_RENDER_HMAC_SECRET=<your-64-char-hex-secret>

curl ile Test Etme

bash
# Bash script to generate signed URL
SECRET="your-hmac-secret-here"
API_KEY="sk_quick_123abc"  # Your full API key

# canonical query: sorted "k=v" pairs, every param except `s` (key included)
CANONICAL="key=${API_KEY}&template=default&title=Test"
MESSAGE=$(printf 'GET/api/v1/og\n%s' "$CANONICAL")
SIGNATURE=$(printf '%s' "$MESSAGE" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)

curl "http://localhost:8000/api/v1/og?template=default&title=Test&key=${API_KEY}&s=${SIGNATURE}"

Beklenen: 200 OK görüntü verisiyle (veya şablon parametreleri geçersizse 422).

Tam API Referansı

Ücretsiz deneyin — kayıt gerekmez

Dinamik OG görsellerini saniyeler içinde önizleyin ve test edin.

Ücretsiz OG Görsel Test Aracını Aç

A BeCaSimulations site