HMAC-SHA256 認証仕様
暗号学的HMAC-SHA256署名を使用してSmallPict APIへのリクエストを認証する方法。
SmallPictは、すべてのAPIトラフィックにおいて 暗号学的HMAC-SHA256リクエスト署名 を採用しています。
この規格により、以下のセキュリティが保証されます:
- ペイロードの完全性 (Payload Integrity): リクエストボディとパスが通信経路で改ざんされていないこと。
- リプレイ攻撃耐性 (Anti-Replay Attack): タイムスタンプドリフト検証により、過去に傍受されたリクエストの再利用を防止。
- 秘密鍵の漏洩ゼロ (Zero Secret Exposure): シークレットキーが通信回線上に送信されることは決してありません。
必須HTTPリクエストヘッダー
認証が必要なすべてのAPI呼び出しには、以下の3つのHTTPヘッダーを含める必要があります:
HTTP
X-API-Key: sp_sdk_9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0bX-Timestamp: 1716301234X-Signature: 3a9f8b2c4d6e8a0f1b3c5d7e9f2a4b6c8d0e1f3a5b7c9d1e3f5a7b9c1d3e5f7a| ヘッダー名 | 型 | 説明 |
|---|---|---|
X-API-Key | String | お客様の公開APIキー(例: sp_sdk_..., sp_wp_..., sp_test_...) |
X-Timestamp | Integer String | リクエスト送信時のUnixエポックタイムスタンプ(秒単位) |
X-Signature | Hex String | シークレットキーを用いて計算された64桁の16進数HMAC-SHA256署名 |
署名生成アルゴリズム
ステップ 1: 署名対象文字列 (String-To-Sign) の構築
HTTPメソッド、パス、タイムスタンプ、リクエストボディのSHA-256ハッシュを改行コード (\n) で連結します:
TEXT
{HTTP_METHOD}\n{PATH}\n{TIMESTAMP}\n{BODY_SHA256_HEX}{HTTP_METHOD}: 大文字のHTTP動詞(例:POST,GET,DELETE)。{PATH}: クエリパラメータを除いた正確なリクエストURI(例:/v1/optimize,/v1/quota)。{TIMESTAMP}: ヘッダーX-Timestampと同一の秒単位タイムスタンプ。{BODY_SHA256_HEX}: リクエストボディの生バイトのSHA-256(16進数)。GETや空ボディの場合は空文字列のSHA-256ハッシュを使用:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855。
ステップ 2: HMAC-SHA256 ハッシュの計算
TEXT
Signature = hex(HMAC_SHA256(SecretKey, StringToSign))各言語での署名実装例
Node.js (TypeScript)
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
Python
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
Go
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))}タイムスタンプの許容時間窓 (±300秒)
リプレイ攻撃を防止するため、SmallPictはサーバー時刻との誤差を ±300秒(5分間) に制限しています。
|server_time - X-Timestamp| > 300 となるリクエストは、すべて 401 Unauthorized (ERR_TIMESTAMP_DRIFT) として拒否されます。サーバーのNTP時刻同期をご確認ください。