Xác thực HMAC
Xác thực HMAC là cách FastOG xác minh rằng mọi yêu cầu hình ảnh OG dựa trên đăng ký đều đến từ máy chủ của chính bạn — các URL đã ký giúp bảo vệ tín dụng của bạn. Đăng ký, sau đó chọn gói API Động (Dynamic API) và tạo khóa API cùng bí mật HMAC trong bảng điều khiển (dashboard); các lần hiển thị API trả phí đều sạch sẽ. Trang này bao gồm các quy tắc phía máy chủ đằng sau hình ảnh Open Graph động, từ việc ký chuỗi truy vấn chuẩn (canonical query signing) đến so sánh thời gian hằng số (constant-time comparison).
Tổng quan
Xác thực chữ ký HMAC-SHA256 đã được thêm vào endpoint /api/v1/og để ngăn chặn việc sử dụng hạn mức API trái phép. Điều này đảm bảo chỉ những yêu cầu từ trang web hợp lệ mới có thể tạo hình ảnh OG.
Kiến trúc
Xác thực hai lớp:
- Chữ ký HMAC (lớp đầu tiên) – Xác nhận yêu cầu đến từ nguồn đáng tin cậy (trang web)
- Khóa API (lớp thứ hai) – Xác định tài khoản người dùng để tính hạn mức kết xuất (render quota)
Một middleware duy nhất (ApiKeyAuth): xác minh chữ ký (tham số truy vấn) và phân giải khóa API để tính hạn mức kết xuất trong một lần xử lý — từ chối nhanh (fail fast) đối với chữ ký không hợp lệ.
Tham số truy vấn, không phải header: Vì hình ảnh OG được tải bởi trình thu thập dữ liệu của mạng xã hội (LinkedIn, Twitter, Facebook) không gửi header tùy chỉnh, chữ ký được truyền qua tham số truy vấn trong URL:
GET /api/v1/og?title=Hello&key=sk_quick_123abc&s=<hmac>
Triển khai
Các tệp đã tạo/sửa đổi
app/Http/Middleware/HmacSignature.php- Middleware mới xác thực chữ ký HMAC (tham số truy vấn)routes/api.php- Thêm middleware HMAC vào tuyến OGbootstrap/app.php- Đăng ký bí danh middlewarehmacapp/Models/ApiKey.php- Thêm trườnghmac_secret(bí mật riêng cho từng người dùng)app/Http/Controllers/DashboardController.php- Tạo bí mật HMAC khi tạo khóa APIdatabase/migrations/2026_08_03_214931_add_hmac_secret_to_api_keys_table.php- Migration cho cột hmac_secretresources/js/Pages/Dashboard/ApiKeys.svelte- Hiển thị bí mật HMAC cho người dùng (một lần)docs/hmac-signature-guide.md- Hướng dẫn triển khai phía máy kháchtests/Feature/HmacSignatureTest.php- 8 bài kiểm thử đạt yêu cầu
Thuật toán chữ ký
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 là một phần của truy vấn chuẩn, do đó chữ ký được gắn với khóa API. Thay đổi bất kỳ tham số nào (tiêu đề, mẫu, hình ảnh, …) hoặc chính khóa đó sẽ làm thay đổi chuẩn → chữ ký không khớp → 401 → không hiển thị, không tính hạn mức. Các tham số giống nhau → chữ ký giống nhau → URL giống nhau → có thể lưu cache ở biên vĩnh viễn, phát lại mà không tính hạn mức.
Các tính năng bảo mật
- Chữ ký định địa chỉ nội dung - Bất kỳ thay đổi tham số nào → 401 → không hiển thị, không tính hạn mức (bản thân URL là bằng chứng)
- Không có dấu thời gian - URL được nhúng trong HTML công khai và lưu cache vĩnh viễn; chúng phải tồn tại lâu hơn 5 phút
- So sánh thời gian hằng số (
hash_equals()) - Ngăn chặn tấn công timing - Khóa API trong thông điệp - Chữ ký được gắn với khóa; đổi khóa sẽ thất bại
- Riêng theo đường dẫn - Chữ ký chỉ hợp lệ cho đường dẫn chính xác
- Riêng theo phương thức - Chữ ký GET sẽ không hoạt động cho POST
- Bí mật riêng cho từng người dùng - Mỗi khóa API có bí mật HMAC riêng, có thể thu hồi độc lập (xoay vòng = công tắc ngắt cho các URL bị rò rỉ)
Cấu hình
Tạo Secret (Tự động)
Secret HMAC được tạo tự động khi tạo khóa API trong bảng điều khiển (dashboard). Secret này chỉ được hiển thị một lần cùng với khóa API, sau đó không bao giờ hiển thị lại.
Tạo thủ công (nếu cần):
openssl rand -hex 32
Lược đồ cơ sở dữ liệu
ALTER TABLE api_keys ADD COLUMN hmac_secret VARCHAR(64)
COMMENT 'HMAC-SHA256 secret for signing OG image URLs (hex, 64 chars)';
Biến môi trường
Không cần biến môi trường toàn cục - secret được lưu trữ riêng cho từng người dùng trong bảng api_keys.
Ví dụ sử dụng (Backend của trang web)
// 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}" />
Phản hồi lỗi
| Trạng thái | Mã | Mô tả |
|---|---|---|
| 401 | missing_signature |
Thiếu tham số truy vấn key hoặc s |
| 401 | invalid_signature |
Chữ ký không khớp với truy vấn chuẩn (bị can thiệp hoặc sai bí mật) |
Kiểm thử
Chạy các bài kiểm thử:
php artisan test --filter HmacSignatureTest
Tất cả 8 bài kiểm thử đều vượt qua:
- ✅ Yêu cầu không có chữ ký bị từ chối
- ✅ Yêu cầu thiếu tham số khóa bị từ chối
- ✅ Tham số nội dung bị thay đổi (tiêu đề bị sửa) bị từ chối
- ✅ Tham số được thêm vào bị từ chối
- ✅ Chữ ký không hợp lệ bị từ chối
- ✅ Chữ ký hợp lệ vượt qua kiểm tra HMAC
- ✅ Chữ ký cụ thể theo đường dẫn
- ✅ Chữ ký cụ thể theo phương thức
- ✅ Các khóa API khác nhau có bí mật khác nhau
Tác động đến hiệu suất
- Không đáng kể - Việc tính toán HMAC-SHA256 chỉ mất khoảng 0,1ms cho mỗi yêu cầu
- Không có thêm lệnh gọi mạng nào - Việc xác thực được thực hiện cục bộ
- Thất bại nhanh - Chữ ký không hợp lệ bị từ chối trước khi tra cứu khóa API
Các lưu ý về bảo mật
- Không bao giờ để lộ bí mật trong mã phía máy khách - Chỉ ký trên máy chủ
- Sử dụng HTTPS - HMAC bảo vệ chống lại các yêu cầu trái phép, HTTPS bảo vệ chống lại tấn công trung gian (MITM)
- Định kỳ xoay vòng bí mật - Đặc biệt nếu bị xâm phạm (yêu cầu tạo lại khóa API)
- Giám sát các chữ ký thất bại - Các lần thất bại lặp lại có thể cho thấy các cuộc tấn công
- Cô lập theo từng người dùng - Mỗi khóa API có bí mật HMAC riêng, việc xâm phạm một khóa không ảnh hưởng đến các khóa khác
Tài liệu tham khảo
- Hướng dẫn chữ ký HMAC dành cho khách hàng
- Hướng dẫn khởi động nhanh
- OWASP: Bảng ghi nhớ lưu trữ mật mã
- PHP: hash_hmac()