HMAC を使った API 署名システムの完全設計図
API の安全性を高めるために「HMAC 署名」を導入することは、業務システムにおいて非常に効果的です。 署名の仕組みを正しく設計すれば、外部からの改ざんや不正アクセスを強力に防止できます。
ここでは、初心者でも理解できるように ステップバイステップで思考しながら、 そのまま実務に導入できる 完全な API 署名システム設計図をまとめます。
HMAC 署名システムの目的
改ざん防止
API リクエストが第三者によって書き換えられていないことを保証します。
正当な送信者の証明
秘密鍵を知っている送信者だけが正しい署名を生成できます。
API の安全性向上
Webhook、外部連携、IoT など「外部からの入力」が多いシステムで必須です。
HMAC 署名システムの全体構成
API 署名システムは次の 4 つの要素で構成されます。
- 署名対象メッセージの正規化
- HMAC 署名の生成
- 署名の送信(HTTP ヘッダーなど)
- 署名の検証(サーバー側)
この 4 つが揃って初めて「安全な署名システム」が成立します。
ステップ 1:署名対象メッセージの正規化
署名は「メッセージの内容が完全一致していること」が前提です。
例:
amount=100¤cy=JPY
順番が変わると署名が変わります。
currency=JPY&amount=100 ← 別の署名になる
そのため、署名対象のメッセージは必ず 正規化(Normalization) します。
ステップ 2:HMAC 署名を生成する
C# では HMACSHA256 を使うのが一般的です。
HMAC 署名生成ユーティリティ(テンプレート)
using System;
using System.Security.Cryptography;
using System.Text;
public static class HmacUtility
{
public static string GenerateHmacSha256(string message, string secretKey)
{
var keyBytes = Encoding.UTF8.GetBytes(secretKey);
var messageBytes = Encoding.UTF8.GetBytes(message);
using var hmac = new HMACSHA256(keyBytes);
var hashBytes = hmac.ComputeHash(messageBytes);
return Convert.ToBase64String(hashBytes);
}
public static string GenerateHmacSha256Hex(string message, string secretKey)
{
var keyBytes = Encoding.UTF8.GetBytes(secretKey);
var messageBytes = Encoding.UTF8.GetBytes(message);
using var hmac = new HMACSHA256(keyBytes);
var hashBytes = hmac.ComputeHash(messageBytes);
return BitConverter.ToString(hashBytes).Replace("-", "").ToLower();
}
}
C#ステップ 3:署名を API リクエストに付与する
署名は通常、HTTP ヘッダーに付与します。
例:
X-Signature: <署名>
X-Timestamp: <UNIX時間>
タイムスタンプを付与する理由: リプレイ攻撃(過去の署名を再利用する攻撃)を防ぐためです。
ステップ 4:サーバー側で署名を検証する
署名検証は次の手順で行います。
- クライアントから受け取ったメッセージを正規化する
- 同じ秘密鍵で HMAC 署名を生成する
- クライアントの署名と一致するか比較する
- タイムスタンプが許容範囲か確認する(例:±5分)
署名検証ユーティリティ(テンプレート)
using System;
using System.Security.Cryptography;
using System.Text;
public static class HmacValidator
{
public static bool ValidateSignature(string message, string receivedSignature, string secretKey)
{
string expectedSignature = HmacUtility.GenerateHmacSha256Hex(message, secretKey);
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expectedSignature),
Encoding.UTF8.GetBytes(receivedSignature)
);
}
public static bool ValidateTimestamp(long timestamp, int allowedSeconds = 300)
{
long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
return Math.Abs(now - timestamp) <= allowedSeconds;
}
}
C#ステップ 5:署名検証の実務コード例
string message = "amount=100¤cy=JPY";
string receivedSignature = request.Headers["X-Signature"];
long timestamp = long.Parse(request.Headers["X-Timestamp"]);
if (!HmacValidator.ValidateTimestamp(timestamp))
{
throw new SecurityException("Timestamp is too old");
}
bool valid = HmacValidator.ValidateSignature(message, receivedSignature, secretKey);
if (!valid)
{
throw new SecurityException("Invalid signature");
}
Console.WriteLine("署名検証成功");
C#セキュリティの深掘りポイント(重要)
秘密鍵は十分に長くする
短い鍵は総当たり攻撃に弱いです。 推奨:32〜64 バイト以上 生成方法:暗号学的乱数
メッセージの正規化は必須
順番が変わると署名が一致しません。
タイムスタンプでリプレイ攻撃を防ぐ
署名が正しくても、過去の署名を再利用されると危険です。
HMAC は暗号化ではない
メッセージを隠したい場合は AES などの暗号化が必要です。
HMAC 署名で絶対にやってはいけないこと
- 秘密鍵を短くする
- 秘密鍵をコードにベタ書きする
- 秘密鍵をログに出力する
- メッセージを正規化せずに署名する
- SHA1 を使う(脆弱)
- 署名を平文で保存する
これらはすべて重大なセキュリティ事故につながります。
実務的ベストプラクティス
- HMAC-SHA256 を使う
- 秘密鍵は KeyVault や環境変数で管理する
- メッセージを正規化する
- タイムスタンプを必ず付与する
- HTTPS を必ず使う
- 署名をログに出力しない
まとめ:HMAC 署名システムは「API の改ざん防止の中心技術」
HMAC 署名システムの本質は次の 3 つです。
- 秘密鍵を使ってメッセージに署名する
- 改ざんされていないことを保証する
- 正しい送信者であることを証明する
これらを正しく実装することで、 攻撃者がメッセージを偽造したり書き換えたりするのを極端に難しくできます。
読者のみなさんが業務システムを開発する際、 この設計図を使うだけでセキュリティレベルが大幅に向上し、 ユーザーの安全を確実に守ることができます。
