Οδηγός Υπογραφής HMAC
Αυτός ο οδηγός υπογραφής HMAC σας καθοδηγεί στη δημιουργία υπογραφών HMAC-SHA256 για το FastOG, ώστε να μπορείτε να δημιουργείτε δυναμικές εικόνες Open Graph (εικόνες OG) εν κινήσει — με συνδρομή, δωρεάν λογαριασμό και πρόγραμμα Dynamic API (οι πληρωμένες αποδόσεις είναι καθαρές). Μπορείτε να δημιουργήσετε τη υπογραφή χειροκίνητα σε οποιαδήποτε γλώσσα εδώ, ή να χρησιμοποιήσετε το Δωρεάν Εργαλείο Δοκιμής Εικόνων OG για να δείτε πρώτα τη μορφή URL σε λειτουργία.
Επισκόπηση
Όλα τα αιτήματα προς το /api/v1/og πρέπει να περιλαμβάνουν υπογραφή HMAC-SHA256 για να αποδεικνύεται ότι προέρχονται από τον νόμιμο ιστότοπο. Αυτό αποτρέπει μη εξουσιοδοτημένα μέρη από το να χρησιμοποιούν το όριο κλήσεων του API σας.
Δύο επίπεδα ταυτοποίησης:
- Κλειδί API (στο query:
key=sk_quick_123abc) — προσδιορίζει τον λογαριασμό χρήστη (για τη χρέωση του ορίου απόδοσης) - Υπογραφή HMAC (στο query:
s=<signature>) — αποδεικνύει ότι το αίτημα προήλθε από τον ιστότοπό σας
Πρέπει να ισχύουν και τα δύο. Το κλειδί API από μόνο του ΔΕΝ αρκεί.
Γιατί παράμετροι query; Οι εικόνες OG ανακτώνται από crawlers κοινωνικών δικτύων (LinkedIn, Twitter, Facebook) που δεν στέλνουν προσαρμοσμένες κεφαλίδες. Η υπογραφή ΠΡΕΠΕΙ να βρίσκεται στη διεύθυνση URL.
Δημιουργία της Υπογραφής
Αλγόριθμος
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>
Απαιτήσεις
- Μορφή υπογραφής: Συμβολοσειρά hex 64 χαρακτήρων (SHA256)
- Χρησιμοποιήστε το
hash_equals()για σύγκριση σταθερού χρόνου (αποτρέπει επιθέσεις χρονισμού) - Μυστικά ανά χρήστη: Κάθε κλειδί API έχει το δικό του πεδίο
hmac_secret
Παραδείγματα Υλοποιήσεων
Για πλήρη σεμινάρια με πολλαπλά frameworks και μοτίβα, δείτε:
- JavaScript/Node.js Σεμινάριο - Next.js, Express, vanilla Node.js
- Next.js App Router
- Next.js Pages Router
- React
- SvelteKit
- Vue/Nuxt
- Astro
- Express
- Vanilla 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 }}" />
Πλήρες Παράδειγμα: Πρότυπο Laravel 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>
Βέλτιστες Πρακτικές Ασφάλειας
- Αποθηκεύστε τα μυστικά με ασφάλεια - Χρησιμοποιήστε μεταβλητές περιβάλλοντος ή έναν διαχειριστή μυστικών
- Ποτέ μην εκθέτετε μυστικά σε κώδικα από την πλευρά του πελάτη - Υπογράψτε τα URL μόνο στον διακομιστή
- Χρησιμοποιήστε HTTPS - Αποτρέπει επιθέσεις MITM
- Αλλάζετε τα μυστικά περιοδικά - Ειδικά αν έχουν παραβιαστεί
- Παρακολουθείτε τις αποτυχημένες υπογραφές - Οι επαναλαμβανόμενες αποτυχίες μπορεί να υποδηλώνουν επιθέσεις
- Απομόνωση ανά χρήστη - Κάθε κλειδί API έχει το δικό του μυστικό HMAC
Αποκρίσεις Σφαλμάτων
Λείπει Υπογραφή
{
"error": "Missing signature parameters",
"code": "missing_signature",
"required_params": ["s", "key"],
"example": "/api/v1/og?title=Hello&key=sk_quick_123abc&s=<signature>"
}
Μη Έγκυρη Υπογραφή
{
"error": "Invalid signature",
"code": "invalid_signature"
}
Δοκιμές
Δημιουργήστε μια διεύθυνση URL δοκιμής:
# 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>"
Αναμενόμενες αποκρίσεις:
- ✅ Έγκυρη υπογραφή → Επιστρέφει την εικόνα OG (ή σφάλμα επικύρωσης αν λείπουν παράμετροι προτύπου)
- ❌ Μη έγκυρη υπογραφή →
{"error": "...", "code": "invalid_signature"} - ❌ Λείπουν παράμετροι →
{"error": "...", "code": "missing_signature"}
Αντιμετώπιση προβλημάτων
Η υπογραφή δεν ταιριάζει
- Επαληθεύστε ότι χρησιμοποιείτε το σωστό μυστικό HMAC (ελέγξτε το κλειδί API στον πίνακα ελέγχου)
- Ελέγξτε το κανονικό ερώτημα: κάθε παράμετρος ερωτήματος εκτός από το
s(συμπεριλαμβανομένου τουkey), τιμές κωδικοποιημένες με rfc3986, κλειδιά ταξινομημένα λεξικογραφικά - Επαληθεύστε ότι η υπογραφή είναι πεζό δεκαεξαδικό (64 χαρακτήρες)
Λήψη του Μυστικού Κλειδιού HMAC
- Μεταβείτε στο Πίνακας Ελέγχου → Κλειδιά API
- Δημιουργήστε ένα νέο κλειδί API (ή χρησιμοποιήστε ένα υπάρχον)
- Αντιγράψτε το Μυστικό Κλειδί HMAC που εμφανίζεται δίπλα στο κλειδί API (εμφανίζεται μόνο μία φορά!)
- Αποθηκεύστε το με ασφάλεια στις μεταβλητές περιβάλλοντος
# Add to .env
OG_RENDER_HMAC_SECRET=<your-64-char-hex-secret>
Δοκιμή με το curl
# 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}"
Αναμενόμενο αποτέλεσμα: 200 OK με δεδομένα εικόνας (ή 422 εάν οι παράμετροι του προτύπου είναι μη έγκυρες).