HMAC 署名を検証することは「API の改ざん防止と送信者の正当性を保証するための中心技術」です
業務システムや Web API を開発する読者のみなさんにとって、 HMAC(Hash-based Message Authentication Code)署名を正しく検証することは必須のセキュリティ要件です。
署名検証は次の 2 つを保証します。
- メッセージが第三者によって改ざんされていない
- 秘密鍵を知っている正当な送信者によって生成された
ここでは、初心者でも理解できるように ステップバイステップで思考しながら、 実務で使える C# の HMAC 署名検証ユーティリティを丁寧に解説します。
HMAC 署名検証の全体像
HMAC 署名検証は次の 4 ステップで構成されます。
- 署名対象メッセージの正規化
- 受信した署名を取得する
- 同じ秘密鍵で署名を再生成する
- 受信した署名と一致するか比較する(固定時間比較)
この 4 つが揃って初めて「安全な署名検証」が成立します。
HMAC 署名検証が必要な実務例
- Webhook の署名検証(Stripe、Slack、LINE、PayPal など)
- API リクエストの署名検証
- IoT デバイスの通信署名
- 外部サービス連携の改ざん防止
- JWT の HS256 署名検証
HMAC 署名検証のステップ(初心者向けにかみ砕く)
Step 1:署名対象メッセージを正規化する
署名は「メッセージの内容が完全一致していること」が前提です。
例:
amount=100¤cy=JPY
順番が変わると署名が変わります。
そのため、署名対象のメッセージは必ず 正規化(Normalization) します。
Step 2:受信した署名を取得する
署名は通常、HTTP ヘッダーに付与されます。
例:
X-Signature: <署名>
X-Timestamp: <UNIX時間>
タイムスタンプはリプレイ攻撃防止のために使います。
Step 3:同じ秘密鍵で署名を再生成する
送信者と同じ秘密鍵を使って署名を再生成します。
Step 4:署名を固定時間比較で照合する
固定時間比較(FixedTimeEquals)を使うことで、 タイミング攻撃(署名一致の時間差を利用する攻撃)を防ぎます。
実務で使える HMAC 署名検証ユーティリティ(テンプレート)
using System;
using System.Security.Cryptography;
using System.Text;
public static class HmacValidator
{
/// <summary>
/// HMAC-SHA256 署名を検証します。
/// </summary>
public static bool ValidateSignature(string message, string receivedSignature, string secretKey)
{
string expectedSignature = GenerateHmacSha256Hex(message, secretKey);
return CryptographicOperations.FixedTimeEquals(
Encoding.UTF8.GetBytes(expectedSignature),
Encoding.UTF8.GetBytes(receivedSignature)
);
}
/// <summary>
/// タイムスタンプの検証(リプレイ攻撃防止)
/// </summary>
public static bool ValidateTimestamp(long timestamp, int allowedSeconds = 300)
{
long now = DateTimeOffset.UtcNow.ToUnixTimeSeconds();
return Math.Abs(now - timestamp) <= allowedSeconds;
}
private 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#署名検証の実務コード例
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 署名検証で絶対にやってはいけないこと
- 秘密鍵を短くする
- 秘密鍵をコードにベタ書きする
- 秘密鍵をログに出力する
- メッセージを正規化せずに署名する
- SHA1 を使う(脆弱)
- 通常の文字列比較で署名を比較する
これらはすべて重大なセキュリティ事故につながります。
実務的ベストプラクティス
- HMAC-SHA256 を使う
- 秘密鍵は KeyVault や環境変数で管理する
- メッセージを正規化する
- タイムスタンプを必ず付与する
- 固定時間比較を使う
- HTTPS を必ず使う
- 署名をログに出力しない
まとめ:HMAC 署名検証は「API の改ざん防止の中心技術」
HMAC 署名検証の本質は次の 3 つです。
- 秘密鍵を使って署名を再生成する
- 改ざんされていないことを保証する
- 正しい送信者であることを証明する
これらを正しく実装することで、 攻撃者がメッセージを偽造したり書き換えたりするのを極端に難しくできます。
読者のみなさんが業務システムを開発する際、 このユーティリティを使うだけでセキュリティレベルが大幅に向上し、 ユーザーの安全を確実に守ることができます。
