C# Tips | セキュリティ:HMAC署名を検証する

C# C#
スポンサーリンク
スポンサーリンク

HMAC 署名を検証することは「API の改ざん防止と送信者の正当性を保証するための中心技術」です

業務システムや Web API を開発する読者のみなさんにとって、 HMAC(Hash-based Message Authentication Code)署名を正しく検証することは必須のセキュリティ要件です。

署名検証は次の 2 つを保証します。

  • メッセージが第三者によって改ざんされていない
  • 秘密鍵を知っている正当な送信者によって生成された

ここでは、初心者でも理解できるように ステップバイステップで思考しながら、 実務で使える C# の HMAC 署名検証ユーティリティを丁寧に解説します。

HMAC 署名検証の全体像

HMAC 署名検証は次の 4 ステップで構成されます。

  1. 署名対象メッセージの正規化
  2. 受信した署名を取得する
  3. 同じ秘密鍵で署名を再生成する
  4. 受信した署名と一致するか比較する(固定時間比較)

この 4 つが揃って初めて「安全な署名検証」が成立します。

HMAC 署名検証が必要な実務例

  • Webhook の署名検証(Stripe、Slack、LINE、PayPal など)
  • API リクエストの署名検証
  • IoT デバイスの通信署名
  • 外部サービス連携の改ざん防止
  • JWT の HS256 署名検証

HMAC 署名検証のステップ(初心者向けにかみ砕く)

Step 1:署名対象メッセージを正規化する

署名は「メッセージの内容が完全一致していること」が前提です。

例:

amount=100&currency=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 つです。

  • 秘密鍵を使って署名を再生成する
  • 改ざんされていないことを保証する
  • 正しい送信者であることを証明する

これらを正しく実装することで、 攻撃者がメッセージを偽造したり書き換えたりするのを極端に難しくできます。

読者のみなさんが業務システムを開発する際、 このユーティリティを使うだけでセキュリティレベルが大幅に向上し、 ユーザーの安全を確実に守ることができます。

タイトルとURLをコピーしました