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:
- API Anahtarı (sorguda:
key=sk_quick_123abc) — kullanıcı hesabını tanımlar (işleme kotası muhasebesi için) - 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_secretalanı vardır
Örnek Uygulamalar
Birden fazla framework ve desen içeren kapsamlı eğitimler için bkz.:
- JavaScript/Node.js Eğitimi - Next.js, Express, saf Node.js
- Next.js App Router
- Next.js Pages Router
- React
- SvelteKit
- Vue/Nuxt
- Astro
- Express
- Saf Node.js
- PHP/Laravel
- Python
- Ruby/Rails
JavaScript (Node.js / Frontend)
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)
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
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
{{-- 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ı
- Sırları güvenli şekilde saklayın - Ortam değişkenleri veya bir sır yöneticisi kullanın
- Sırları asla istemci tarafı kodda açığa çıkarmayın - URL'leri yalnızca sunucuda imzalayın
- HTTPS kullanın - MITM saldırılarını önler
- Sırları periyodik olarak döndürün - Özellikle tehlikeye girildiyse
- Başarısız imzaları izleyin - Tekrarlanan başarısızlıklar saldırı göstergesi olabilir
- Kullanıcı başına izolasyon - Her API anahtarının kendi HMAC sırrı vardır
Hata Yanıtları
Eksik İmza
{
"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
{
"error": "Invalid signature",
"code": "invalid_signature"
}
Test Etme
Bir test URL’si oluşturun:
# 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
- Doğru HMAC gizli anahtarını kullandığınızı doğrulayın (paneldeki API anahtarını kontrol edin)
- Kanonik sorguyu kontrol edin:
shariç tüm sorgu parametreleri (keydahil), değerler rfc3986 kodlu, anahtarlar sözlükbilimsel olarak sıralanmış - İmzanın küçük harfli onaltılık (64 karakter) olduğunu doğrulayın
HMAC Sırrınızı Alma
- Kontrol Paneli → API Anahtarları bölümüne gidin
- Yeni bir API anahtarı oluşturun (veya mevcut birini kullanın)
- API anahtarının yanında gösterilen HMAC Sırrını kopyalayın (yalnızca bir kez gösterilir!)
- Bunu ortam değişkenlerinizde güvenli bir şekilde saklayın
# Add to .env
OG_RENDER_HMAC_SECRET=<your-64-char-hex-secret>
curl ile Test Etme
# 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).