Skip to content
smallPict

HMAC-SHA256 認証仕様

暗号学的HMAC-SHA256署名を使用してSmallPict APIへのリクエストを認証する方法。

SmallPictは、すべてのAPIトラフィックにおいて 暗号学的HMAC-SHA256リクエスト署名 を採用しています。

この規格により、以下のセキュリティが保証されます:

  1. ペイロードの完全性 (Payload Integrity): リクエストボディとパスが通信経路で改ざんされていないこと。
  2. リプレイ攻撃耐性 (Anti-Replay Attack): タイムスタンプドリフト検証により、過去に傍受されたリクエストの再利用を防止。
  3. 秘密鍵の漏洩ゼロ (Zero Secret Exposure): シークレットキーが通信回線上に送信されることは決してありません。

必須HTTPリクエストヘッダー

認証が必要なすべてのAPI呼び出しには、以下の3つのHTTPヘッダーを含める必要があります:

HTTP
X-API-Key: sp_sdk_9a8b7c6d5e4f3a2b1c0d9e8f7a6b5c4d3e2f1a0bX-Timestamp: 1716301234X-Signature: 3a9f8b2c4d6e8a0f1b3c5d7e9f2a4b6c8d0e1f3a5b7c9d1e3f5a7b9c1d3e5f7a
ヘッダー名型説明
X-API-KeyStringお客様の公開APIキー(例: sp_sdk_..., sp_wp_..., sp_test_...)
X-TimestampInteger Stringリクエスト送信時のUnixエポックタイムスタンプ(秒単位)
X-SignatureHex 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, signature

Go

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時刻同期をご確認ください。