Tiêu chuẩn xác thực HMAC-SHA256
Cách xác thực các yêu cầu gửi đến SmallPict API bằng chữ ký mật mã học HMAC-SHA256.
SmallPict bảo vệ toàn bộ lưu lượng API bằng cơ chế ký yêu cầu mật mã học HMAC-SHA256.
Tiêu chuẩn này mang lại 3 lớp bảo vệ cốt lõi:
- Tính toàn vẹn dữ liệu (Payload Integrity): Nội dung phần thân yêu cầu và đường dẫn URI không thể bị thay đổi trên đường truyền.
- Chống tấn công phát lại (Anti-Replay Attack): Giới hạn chênh lệch thời gian ngăn chặn việc sử dụng lại các yêu cầu bị nghe lén.
- Không để lộ khóa bí mật (Zero Secret Exposure): Khóa Secret Key của bạn không bao giờ được gửi qua đường truyền Internet.
Các tiêu đề HTTP bắt buộc
Mỗi yêu cầu API có xác thực đều phải bao gồm 3 tiêu đề HTTP sau:
X-API-Key: sp_sdk_9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0bX-Timestamp: 1716301234X-Signature: 3a9f8b2c4d6e8a0f1b3c5d7e9f2a4b6c8d0e1f3a5b7c9d1e3f5a7b9c1d3e5f7a| Tiêu đề | Kiểu | Mô tả chi tiết |
|---|---|---|
X-API-Key | Chuỗi | Khóa API công khai của bạn (ví dụ: sp_sdk_..., sp_wp_..., sp_test_...) |
X-Timestamp | Chuỗi số nguyên | Dấu thời gian Unix Epoch tính bằng giây tại thời điểm gửi |
X-Signature | Chuỗi Hex | Chữ ký HMAC-SHA256 mã hóa thập lục phân tạo bởi khóa Secret Key |
Thuật toán tạo chữ ký
Bước 1: Ghép chuỗi chuẩn hóa cần ký (Canonical String-To-Sign)
Nối phương thức HTTP, đường dẫn, dấu thời gian và mã băm SHA-256 của phần thân yêu cầu bằng ký tự xuống dòng (\n):
{HTTP_METHOD}\n{PATH}\n{TIMESTAMP}\n{BODY_SHA256_HEX}{HTTP_METHOD}: Động từ HTTP viết hoa (ví dụ:POST,GET,DELETE).{PATH}: Đường dẫn URI chính xác không bao gồm tham số truy vấn (ví dụ:/v1/optimize,/v1/quota).{TIMESTAMP}: Dấu thời gian Unix tính bằng giây khớp với tiêu đềX-Timestamp.{BODY_SHA256_HEX}: Mã băm SHA-256 dạng hex của phần thân dữ liệu gốc. Đối với yêu cầuGEThoặc không có nội dung body, sử dụng mã băm của chuỗi rỗng:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855.
Bước 2: Tính toán chữ ký HMAC-SHA256
Signature = hex(HMAC_SHA256(SecretKey, StringToSign))Mã nguồn mẫu tạo chữ ký
Node.js (TypeScript)
import crypto from 'node:crypto';
export function signRequest( method: string, path: string, secretKey: string, bodyString: string = '') { const timestamp = Math.floor(Date.now() / 1000).toString(); const bodyHash = crypto.createHash('sha256').update(bodyString).digest('hex'); const stringToSign = `${method.toUpperCase()}\n${path}\n${timestamp}\n${bodyHash}`; const signature = crypto .createHmac('sha256', secretKey) .update(stringToSign) .digest('hex');
return { timestamp, signature };}Python 3
import hmacimport hashlibimport time
def sign_request(method: str, path: str, secret_key: str, body_bytes: bytes = b"") -> tuple[str, str]: timestamp = str(int(time.time())) body_hash = hashlib.sha256(body_bytes).hexdigest() string_to_sign = f"{method.upper()}\n{path}\n{timestamp}\n{body_hash}" signature = hmac.new( secret_key.encode("utf-8"), string_to_sign.encode("utf-8"), hashlib.sha256 ).hexdigest() return timestamp, signatureGo
package main
import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "fmt" "strconv" "time")
func SignRequest(method, path, secretKey string, body []byte) (timestamp string, signature string) { ts := strconv.FormatInt(time.Now().Unix(), 10) h := sha256.Sum256(body) bodyHash := hex.EncodeToString(h[:]) stringToSign := fmt.Sprintf("%s\n%s\n%s\n%s", method, path, ts, bodyHash)
mac := hmac.New(sha256.New, []byte(secretKey)) mac.Write([]byte(stringToSign)) return ts, hex.EncodeToString(mac.Sum(nil))}Giới hạn độ lệch thời gian cho phép (±300 giây)
Để ngăn chặn các cuộc tấn công phát lại, hệ thống cổng API của SmallPict áp dụng giới hạn dung sai thời gian ±300 giây (5 phút).
Các yêu cầu có |server_time - X-Timestamp| > 300 giây sẽ bị từ chối với mã lỗi 401 Unauthorized (ERR_TIMESTAMP_DRIFT). Hãy đảm bảo máy chủ của bạn được đồng bộ thời gian chuẩn qua giao thức NTP.