Θέμα

Οδηγός Υπογραφής HMAC

Αυτός ο οδηγός υπογραφής HMAC σας καθοδηγεί στη δημιουργία υπογραφών HMAC-SHA256 για το FastOG, ώστε να μπορείτε να δημιουργείτε δυναμικές εικόνες Open Graph (εικόνες OG) εν κινήσει — με συνδρομή, δωρεάν λογαριασμό και πρόγραμμα Dynamic API (οι πληρωμένες αποδόσεις είναι καθαρές). Μπορείτε να δημιουργήσετε τη υπογραφή χειροκίνητα σε οποιαδήποτε γλώσσα εδώ, ή να χρησιμοποιήσετε το Δωρεάν Εργαλείο Δοκιμής Εικόνων OG για να δείτε πρώτα τη μορφή URL σε λειτουργία.

Επισκόπηση

Όλα τα αιτήματα προς το /api/v1/og πρέπει να περιλαμβάνουν υπογραφή HMAC-SHA256 για να αποδεικνύεται ότι προέρχονται από τον νόμιμο ιστότοπο. Αυτό αποτρέπει μη εξουσιοδοτημένα μέρη από το να χρησιμοποιούν το όριο κλήσεων του API σας.

Δύο επίπεδα ταυτοποίησης:

  1. Κλειδί API (στο query: key=sk_quick_123abc) — προσδιορίζει τον λογαριασμό χρήστη (για τη χρέωση του ορίου απόδοσης)
  2. Υπογραφή 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 / 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 }}" />

Πλήρες Παράδειγμα: Πρότυπο Laravel Blade

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>

Βέλτιστες Πρακτικές Ασφάλειας

  1. Αποθηκεύστε τα μυστικά με ασφάλεια - Χρησιμοποιήστε μεταβλητές περιβάλλοντος ή έναν διαχειριστή μυστικών
  2. Ποτέ μην εκθέτετε μυστικά σε κώδικα από την πλευρά του πελάτη - Υπογράψτε τα URL μόνο στον διακομιστή
  3. Χρησιμοποιήστε HTTPS - Αποτρέπει επιθέσεις MITM
  4. Αλλάζετε τα μυστικά περιοδικά - Ειδικά αν έχουν παραβιαστεί
  5. Παρακολουθείτε τις αποτυχημένες υπογραφές - Οι επαναλαμβανόμενες αποτυχίες μπορεί να υποδηλώνουν επιθέσεις
  6. Απομόνωση ανά χρήστη - Κάθε κλειδί API έχει το δικό του μυστικό HMAC

Αποκρίσεις Σφαλμάτων

Λείπει Υπογραφή

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

Μη Έγκυρη Υπογραφή

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

Δοκιμές

Δημιουργήστε μια διεύθυνση URL δοκιμής:

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

Αναμενόμενες αποκρίσεις:

  • ✅ Έγκυρη υπογραφή → Επιστρέφει την εικόνα OG (ή σφάλμα επικύρωσης αν λείπουν παράμετροι προτύπου)
  • ❌ Μη έγκυρη υπογραφή → {"error": "...", "code": "invalid_signature"}
  • ❌ Λείπουν παράμετροι → {"error": "...", "code": "missing_signature"}

Αντιμετώπιση προβλημάτων

Η υπογραφή δεν ταιριάζει

  1. Επαληθεύστε ότι χρησιμοποιείτε το σωστό μυστικό HMAC (ελέγξτε το κλειδί API στον πίνακα ελέγχου)
  2. Ελέγξτε το κανονικό ερώτημα: κάθε παράμετρος ερωτήματος εκτός από το s (συμπεριλαμβανομένου του key), τιμές κωδικοποιημένες με rfc3986, κλειδιά ταξινομημένα λεξικογραφικά
  3. Επαληθεύστε ότι η υπογραφή είναι πεζό δεκαεξαδικό (64 χαρακτήρες)

Λήψη του Μυστικού Κλειδιού HMAC

  1. Μεταβείτε στο Πίνακας Ελέγχου → Κλειδιά API
  2. Δημιουργήστε ένα νέο κλειδί API (ή χρησιμοποιήστε ένα υπάρχον)
  3. Αντιγράψτε το Μυστικό Κλειδί HMAC που εμφανίζεται δίπλα στο κλειδί API (εμφανίζεται μόνο μία φορά!)
  4. Αποθηκεύστε το με ασφάλεια στις μεταβλητές περιβάλλοντος
bash
# Add to .env
OG_RENDER_HMAC_SECRET=<your-64-char-hex-secret>

Δοκιμή με το curl

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

Αναμενόμενο αποτέλεσμα: 200 OK με δεδομένα εικόνας (ή 422 εάν οι παράμετροι του προτύπου είναι μη έγκυρες).

Πλήρης Αναφορά API

Δοκιμάστε το δωρεάν — δεν απαιτείται εγγραφή

Προεπισκόπηση και δοκιμή δυναμικών εικόνων OG σε δευτερόλεπτα.

Ανοίξτε τον Δωρεάν Ελεγκτή Εικόνων OG

A BeCaSimulations site