Hashを比較することは「改ざん検知・認証・整合性チェックの中心技術」です
業務システムを開発する読者のみなさんにとって、 ハッシュ値を正しく比較することは、セキュリティの基礎でありながら非常に重要な技術です。
ハッシュ比較は次のような場面で使われます。
- ファイルが改ざんされていないか確認する
- パスワードの検証(高速ハッシュは非推奨だが仕組みは同じ)
- API署名の検証
- データの整合性チェック
- キャッシュキーの一致確認
ここでは、初心者でも理解できるように、 実務で使える C# の「ハッシュ比較ユーティリティ」を ステップバイステップで丁寧に解説します。
ハッシュ比較の基本原理
ハッシュは「指紋」である
ハッシュ値は、入力データから計算される一方向の指紋です。
- 同じ入力 → 同じハッシュ
- 少しでも違う入力 → 全く違うハッシュ
- ハッシュから元のデータを復元することはできない
この性質を利用して「同じデータかどうか」を比較します。
ハッシュ比較で重要なポイント(深掘り)
1. 大文字・小文字の違いに注意
Hex形式のハッシュ値は、 ABCDEF と abcdef は同じ意味です。
比較時は 大文字・小文字を区別しない比較 を使います。
2. 文字列比較ではなく「固定時間比較」が必要な場合がある
セキュリティ用途では、 通常の == 比較はタイミング攻撃に弱いです。
攻撃者は「比較にかかる時間の差」から 一致している部分を推測できてしまいます。
そのため、 CryptographicOperations.FixedTimeEquals を使うことが推奨されます。
3. ハッシュアルゴリズムが異なると比較できない
SHA-256 と SHA-512 は別物です。
- SHA-256 → 64桁のHex
- SHA-512 → 128桁のHex
アルゴリズムが違うと比較できません。
実務で使えるハッシュ比較ユーティリティ(テンプレート)
using System;
using System.Security.Cryptography;
using System.Text;
public static class HashCompareUtility
{
/// <summary>
/// Hex形式のハッシュ値を固定時間比較で検証します。
/// </summary>
public static bool CompareHex(string expectedHex, string actualHex)
{
// 大文字・小文字を統一
string normalizedExpected = expectedHex.ToLower();
string normalizedActual = actualHex.ToLower();
byte[] expectedBytes = Encoding.UTF8.GetBytes(normalizedExpected);
byte[] actualBytes = Encoding.UTF8.GetBytes(normalizedActual);
return CryptographicOperations.FixedTimeEquals(expectedBytes, actualBytes);
}
/// <summary>
/// Base64形式のハッシュ値を固定時間比較で検証します。
/// </summary>
public static bool CompareBase64(string expectedBase64, string actualBase64)
{
byte[] expectedBytes = Encoding.UTF8.GetBytes(expectedBase64);
byte[] actualBytes = Encoding.UTF8.GetBytes(actualBase64);
return CryptographicOperations.FixedTimeEquals(expectedBytes, actualBytes);
}
}
C#ハッシュ比較の使い方(例題)
例1:SHA-256ハッシュを比較する
string expected = "9f2c4a..."; // 保存していたハッシュ値
string actual = FileHashUtility.ComputeSha256Hex("sample.txt");
bool match = HashCompareUtility.CompareHex(expected, actual);
if (match)
{
Console.WriteLine("ファイルは正しい状態です。");
}
else
{
Console.WriteLine("ファイルが改ざんされている可能性があります。");
}
C#例2:API署名の比較(Base64)
string expectedSignature = request.Headers["X-Signature"];
string actualSignature = HmacUtility.GenerateHmacSha256Base64(message, secretKey);
if (!HashCompareUtility.CompareBase64(expectedSignature, actualSignature))
{
throw new SecurityException("署名が一致しません");
}
C#固定時間比較の深掘り(重要)
なぜ固定時間比較が必要なのか
通常の文字列比較は、 一致している部分が多いほど比較時間が短くなる傾向があります。
攻撃者はこの「時間差」を利用して、 署名やハッシュの一部を推測することができます。
固定時間比較は、 一致していてもしていなくても、比較にかかる時間が同じ になるように設計されています。
これにより、 タイミング攻撃を防ぐことができます。
ハッシュ比較で絶対にやってはいけないこと
- 大文字・小文字を区別して比較する
- 通常の
==比較を使う(セキュリティ用途では危険) - アルゴリズムが違うハッシュを比較する
- ハッシュ値をログにそのまま出力する
- ハッシュ値を「暗号化」と誤解する
これらはセキュリティ事故の原因になります。
実務的ベストプラクティス
- Hex形式のハッシュは小文字に統一して比較する
- セキュリティ用途では必ず固定時間比較を使う
- ハッシュ値は安全な場所に保存する
- ハッシュ値を扱うコードはログに出力しない
- ハッシュアルゴリズムを統一する(SHA-256ならSHA-256で揃える)
まとめ:ハッシュ比較は「改ざん検知の最後の砦」
ハッシュ比較の本質は次の3つです。
- 同じハッシュ値なら同じデータである
- 少しでも違えば全く違うハッシュになる
- セキュリティ用途では固定時間比較が必須
このユーティリティを使うことで、 業務システムの改ざん検知・署名検証・整合性チェックを 安全かつ確実に行えるようになります。
関連トピック(Guided Links)
- SHA-256ハッシュ生成
- SHA-512ハッシュ生成
- HMAC署名の検証
- ファイルハッシュ計算
