ToolDeck

PythonでSHA-256 — hashlibガイド

·DevOps Engineer & Python Automation Specialist·レビュー担当Maria Santos·公開日

無料の SHA-256ハッシュ生成ツール をブラウザで直接使用 — インストール不要。

SHA-256ハッシュ生成ツール をオンラインで試す →

デプロイメントパイプラインを構築するとき、ファイルのチェックサム検証、Webhookペイロードへの署名、またはキャッシュキーのフィンガープリント計算が必要になる場面は必ずあります。PythonのSHA-256ハッシュは標準ライブラリの hashlib モジュールですべてのケースを処理できます — しかも既にインストール済みです。 hashlib.sha256() はCPythonでOpenSSLの実装をラップしているため、高速でかつFIPS準拠です。コードを書かずに手軽にハッシュを計算したい場合は、 オンラインSHA-256ハッシュジェネレーター で即座に結果が得られます。すべてのサンプルはPython 3.9以降を対象としています。

  • hashlib.sha256(data).hexdigest()がbytesをハッシュする標準的な方法 — stdlibに含まれOpenSSLを使用。
  • 文字列は事前にbytesにエンコードが必要:hashlib.sha256("text".encode("utf-8"))。
  • ファイルのチェックサムは.update()でチャンク単位に投入 — 大きなファイルを一度にメモリに読み込まない。
  • HMAC-SHA256にはhmacモジュールが必要:hmac.new(key, msg, hashlib.sha256) — SHA-256単体では鍵を持たない。

SHA-256ハッシュとは?

SHA-256(Secure Hash Algorithm, 256ビット)は任意長の入力を受け取り、固定された256ビット(32バイト)のダイジェストを生成します。同じ入力は常に同じ出力を生成しますが、入力の1ビットが変化するだけで全く異なるハッシュが生成されます — この特性はアバランシェ効果と呼ばれます。SHA-256はNISTによって標準化されたSHA-2ファミリーの一部であり、TLS証明書のフィンガープリント、GitのコミットID、Bitcoinのブロックヘッダー、ファイル整合性検証の基盤となっています。このアルゴリズムはMerkle-Damgård構造を使用し、64回の圧縮ラウンドで256ビットの出力を生成します。

Before · text
After · text
deployment-v4.2.1
a1f7c3d8e9b2...27ae41e4649b (64文字の16進数)

上記の16進数ダイジェストが標準的な表現形式です — 1バイトをハッシュしても、ディスクイメージ全体をハッシュしても、常に同じ長さの64文字の16進数になります。

hashlib.sha256() — 標準ライブラリを使ったアプローチ

hashlib モジュールはすべてのPythonインストールに同梱されています — pip install は不要です。 bytes 引数を指定して hashlib.sha256() を呼び出してハッシュオブジェクトを生成し、 .hexdigest() (16進数文字列)または .digest() (生のバイト列)で結果を取得します。関数名は小文字の sha256であり、SHA256ではありません。

Python 3.9+ — 最小構成のSHA-256ハッシュ
import hashlib

# バイト文字列を直接ハッシュ
digest = hashlib.sha256(b"deployment-v4.2.1").hexdigest()
print(digest)
# a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db

hashlib.sha256() でよくある間違いは、 bytes ではなく str を渡してしまうことです。Pythonの文字列はUnicodeであり、ハッシュ関数は生のバイト列を対象とします。ハッシュの前に .encode("utf-8") を呼び出す必要があります。初めて使う場合はほぼ全員がこの点でつまずきます。

Python 3.9+ — 文字列のハッシュ
import hashlib

# ハッシュ前に文字列をbytesにエンコードする必要がある
config_key = "redis://cache.internal:6379/0"
digest = hashlib.sha256(config_key.encode("utf-8")).hexdigest()
print(digest)
# 7d3f8c2a1b9e4f5d6c8a7b3e2f1d9c4a5b8e7f6d3c2a1b9e4f5d6c8a7b3e2f1d

.update() メソッドはデータを逐次的に投入できます。 h.update(a); h.update(b) hashlib.sha256(a + b) と同等です。これがファイル全体をメモリに読み込まずにチャンク単位でハッシュする方法です。

Python 3.9+ — update()を使ったインクリメンタルハッシュ
import hashlib

h = hashlib.sha256()
h.update(b"request_id=req_7f3a91bc")
h.update(b"&timestamp=1741614120")
h.update(b"&amount=4999")
print(h.hexdigest())
# hashlib.sha256(b"request_id=req_7f3a91bc&timestamp=1741614120&amount=4999").hexdigest()と同等
注意:.digest() は生の32バイトを返します。 .hexdigest() は64文字の16進数文字列を返します。 HMAC入力、Base64エンコード、バイナリプロトコルへの投入には .digest() を使用してください。 ログ記録、データベースカラム、チェックサム比較には .hexdigest() を使用してください。

HMAC-SHA256 — hmacモジュールを使った鍵付きハッシュ

SHA-256単体では秘密鍵の概念がありません — 同じ入力があれば誰でも同じハッシュを計算できます。メッセージが特定の送信者から来たことを証明する必要がある場合(Webhook検証、APIリクエスト署名、トークン認証)はHMACが必要です。 hmac モジュールはPythonの標準ライブラリに含まれており、鍵をハッシュ処理に組み込むため、鍵を持つ者だけが同じダイジェストを生成・検証できます。

Python 3.9+ — 基本的なHMAC-SHA256
import hmac
import hashlib

# Webhook署名の検証
secret_key = b"whsec_9f3a7b2e1d4c8a5b"
payload = b'{"event":"invoice.paid","invoice_id":"inv_8d2c","amount":14900}'

signature = hmac.new(secret_key, payload, hashlib.sha256).hexdigest()
print(signature)
# 64文字の16進数HMAC-SHA256ダイジェスト

受信したHMACの検証には == 演算子ではなく hmac.compare_digest() を使用する必要があります。等値演算子は最初に不一致のバイトで短絡評価します。攻撃者はレスポンスタイムを計測して署名を1バイトずつ推測できます。 compare_digest() は不一致の位置に関係なく一定時間で実行されます。

Python 3.9+ — Webhook署名の検証
import hmac
import hashlib

def verify_webhook(payload: bytes, received_sig: str, secret: bytes) -> bool:
    """定時間比較によるWebhook署名の検証。"""
    expected = hmac.new(secret, payload, hashlib.sha256).hexdigest()
    return hmac.compare_digest(expected, received_sig)

# Stripeスタイルのwebhook検証をシミュレート
incoming_payload = b'{"event":"payment.completed","amount":4999}'
incoming_signature = "a1b2c3d4e5f6..."  # X-SignatureヘッダーからHMAC署名を取得
webhook_secret = b"whsec_9f3a7b2e1d4c"

if verify_webhook(incoming_payload, incoming_signature, webhook_secret):
    print("署名有効 — イベントを処理します")
else:
    print("署名不一致 — リクエストを拒否します")

HMAC-SHA256によるリクエスト署名

APIリクエスト署名も同じ原理に従います:リクエストの構成要素(メソッド、パス、タイムスタンプ、ボディハッシュ)から正規文字列を構築し、秘密鍵で署名します。AWS Signature V4、Stripe、GitHubのWebhookはすべてこのパターンのバリエーションを使用しています。

Python 3.9+ — HMAC-SHA256を使ったAPIリクエスト署名
import hmac
import hashlib
import time

def sign_request(method: str, path: str, body: bytes, secret: bytes) -> str:
    """APIリクエスト用のHMAC-SHA256署名を生成。"""
    timestamp = str(int(time.time()))
    body_hash = hashlib.sha256(body).hexdigest()

    # 正規文字列:メソッド + パス + タイムスタンプ + ボディハッシュ
    canonical = f"{method}\n{path}\n{timestamp}\n{body_hash}"
    signature = hmac.new(secret, canonical.encode("utf-8"), hashlib.sha256).hexdigest()

    return f"ts={timestamp},sig={signature}"

# 使用例
api_secret = b"sk_live_9f3a7b2e1d4c8a5b6e7f"
request_body = b'{"customer_id":"cust_4f2a","plan":"enterprise"}'
auth_header = sign_request("POST", "/api/v2/subscriptions", request_body, api_secret)
print(f"Authorization: HMAC-SHA256 {auth_header}")
# Authorization: HMAC-SHA256 ts=1741614120,sig=7d3f8c2a...

Base64エンコードされたHMAC-SHA256

一部のAPI(AWS Signature V4、各種ペイメントゲートウェイ)はHMACの結果を16進数ではなくBase64エンコードされた文字列として期待します。違い:16進数は64文字、Base64は同じ32バイトダイジェストに対して44文字を使用します。

Python 3.9+ — Base64エンコードされたHMAC-SHA256
import hmac
import hashlib
import base64

secret = b"webhook_secret_9f3a"
message = b"POST /api/v2/events 1741614120"

# 16進数出力:64文字
hex_sig = hmac.new(secret, message, hashlib.sha256).hexdigest()
print(f"Hex:    {hex_sig}")

# Base64出力:44文字(短く、HTTPヘッダーでよく使われる)
raw_sig = hmac.new(secret, message, hashlib.sha256).digest()
b64_sig = base64.b64encode(raw_sig).decode("ascii")
print(f"Base64: {b64_sig}")

datetime、UUID、カスタムオブジェクトのハッシュ

SHA-256は生のバイト列を対象とするため、bytesでない型 — datetime UUID、データクラス、Pydanticモデル — はハッシュ前にバイト列にシリアライズする必要があります。自動変換はありません。正規表現を選択します。 システム間で決定的なハッシュを行うには、明示的なエンコーディングと安定したシリアライズ形式を使用してください(datetimeにはISO 8601、UUIDには標準文字列形式、dictには辞書順ソートJSON)。

Python 3.9+ — datetimeとUUIDのハッシュ
import hashlib
import uuid
from datetime import datetime, timezone

# datetime — 移植性のために明示的なUTCオフセットを使ったISO 8601形式
event_time = datetime(2026, 3, 28, 12, 0, 0, tzinfo=timezone.utc)
time_hash = hashlib.sha256(event_time.isoformat().encode("utf-8")).hexdigest()
print(f"datetimeハッシュ: {time_hash[:16]}...")

# UUID — 標準文字列形式(小文字、ハイフン付き)をハッシュ
record_id = uuid.uuid4()
uuid_hash = hashlib.sha256(str(record_id).encode("utf-8")).hexdigest()
print(f"UUIDハッシュ: {uuid_hash[:16]}...")

カスタムオブジェクトの場合、ハッシュ前に正規バイト列表現にシリアライズします。 辞書風オブジェクトにはソートキーを使ったJSONが適しています:

Python 3.9+ — カスタムオブジェクトのハッシュ
import hashlib
import json
from dataclasses import dataclass, asdict

@dataclass
class Event:
    id: str
    type: str
    amount: int
    timestamp: str

def hash_event(event: Event) -> str:
    """決定性のためにソートキーJSONを使ってデータクラスインスタンスをハッシュ。"""
    canonical = json.dumps(asdict(event), sort_keys=True, separators=(",", ":"))
    return hashlib.sha256(canonical.encode("utf-8")).hexdigest()

e = Event(id="evt_4f2a", type="payment.completed", amount=4999, timestamp="2026-03-28T12:00:00Z")
print(hash_event(e))  # 実行やマシンをまたいで安定した値
注意:JSONシリアライズされたオブジェクトをハッシュする際は常にdictのキーをソートしてください(sort_keys=True)。dictの挿入順序はPython 3.7以降で保持されますが、シリアライズ経路によって異なる場合があり、同一データに対して異なるハッシュが生成される可能性があります。

SHA-256ファイルチェックサム — ダウンロードとアーティファクトの検証

ファイルのSHA-256チェックサムを計算することはこのアルゴリズムの最も一般的な用途の一つです。Goバイナリのリリースページ、Pythonのwheelファイル、Dockerイメージマニフェスト、ファームウェアアップデートなど至る所で使われています。重要なのはファイル全体を一度に読み込まずにチャンク単位で読み込むことです — 2 GBのISOイメージのハッシュ計算のためだけに2 GBのRAMを必要とすべきではありません。

Python 3.9+ — ファイルのSHA-256チェックサム(チャンク読み込み)
import hashlib

def sha256_checksum(filepath: str, chunk_size: int = 8192) -> str:
    """メモリ節約のためチャンク読み込みでファイルのSHA-256ハッシュを計算。"""
    h = hashlib.sha256()
    with open(filepath, "rb") as f:
        for chunk in iter(lambda: f.read(chunk_size), b""):
            h.update(chunk)
    return h.hexdigest()

# リリースアーティファクトをハッシュ
checksum = sha256_checksum("/tmp/release-v4.2.1.tar.gz")
print(f"SHA-256: {checksum}")

Python 3.11では hashlib.file_digest() が追加されました。内部でチャンク読み込みを行い、対応プラットフォームではゼロコピー最適化が使われる場合があります。3.11以降を使用している場合は手動ループより優先して使用してください。

Python 3.11+ — hashlib.file_digest()
import hashlib

with open("/tmp/release-v4.2.1.tar.gz", "rb") as f:
    digest = hashlib.file_digest(f, "sha256")

print(digest.hexdigest())

既知のチェックサムに対してダウンロードファイルを検証

Python 3.9+ — チェックサム検証
import hashlib
import hmac as hmac_mod  # compare_digestのみのインポート

def verify_checksum(filepath: str, expected_hex: str) -> bool:
    """定時間比較を使ったSHA-256チェックサムの検証。"""
    h = hashlib.sha256()
    with open(filepath, "rb") as f:
        for chunk in iter(lambda: f.read(8192), b""):
            h.update(chunk)
    return hmac_mod.compare_digest(h.hexdigest(), expected_hex.lower())

# リリースアーティファクトを検証
expected = "a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db"
if verify_checksum("/tmp/release-v4.2.1.tar.gz", expected):
    print("チェックサム一致 — ファイルは完全です")
else:
    print("チェックサム不一致 — ファイルが破損または改ざんされている可能性があります")
注意:秘密鍵が関係しない場合でも、チェックサム比較には常に hmac.compare_digest() を使用してください。定時間比較はタイミングベースの情報漏洩を防ぎます。 == 演算子は機能上は問題ありませんが、セキュリティ上重要なコンテキストでは安全ではありません。

Base64エンコードを使ったSHA-256

一部のプロトコルはSHA-256ダイジェストを16進数ではなくBase64文字列として期待します。 HTTPヘッダーの Content-Digest Integrity (ブラウザのサブリソース整合性)はBase64を使用し、JWT署名はBase64urlエンコードされています。 コツは16進数文字列ではなく生の .digest() バイト列をBase64エンコードすることです。

Python 3.9+ — Base64エンコードされたSHA-256
import hashlib
import base64

data = b"integrity check payload"

# 正しい方法:生バイトのBase64(32バイト → 44文字のBase64)
raw_digest = hashlib.sha256(data).digest()
b64_digest = base64.b64encode(raw_digest).decode("ascii")
print(f"sha256-{b64_digest}")
# sha256-<44文字>

# 誤った方法:16進数文字列のBase64(64 ASCIIバイト → 88文字のBase64 — サイズが倍になる)
hex_digest = hashlib.sha256(data).hexdigest()
wrong = base64.b64encode(hex_digest.encode()).decode()
print(f"誤った長さ: {len(wrong)}文字")  # 88 — APIが期待する値ではない
警告:生バイト列ではなく16進数文字列をBase64エンコードすると、期待される長さの2倍の出力が生成されます。APIはそれを拒否しますが、エラーメッセージには原因が示されないことがほとんどです。Base64エンコード前には常に.hexdigest()ではなく.digest()を呼び出してください。

hashlib.sha256() リファレンス

SHA-256ハッシュオブジェクトのコンストラクタとメソッド:

パラメータ / メソッド
説明
data (位置引数)
bytes
初期ハッシュデータ — コンストラクタ直後に update(data) を呼び出すのと同等
.update(data)
bytes
追加のバイト列をハッシュ状態に投入 — チャンク入力のために複数回呼び出し可能
.digest()
→ bytes
生の32バイトバイナリダイジェストを返す — HMAC入力、バイナリプロトコル、Base64エンコードに使用
.hexdigest()
→ str
64文字の小文字16進数文字列を返す — チェックサムや検証の標準的な表現形式
.copy()
→ ハッシュオブジェクト
現在のハッシュ状態のクローンを返す — 最初からハッシュし直すことなくダイジェストを分岐できる
hashlib.sha256()
コンストラクタ
CPythonではOpenSSLを使用する新しいSHA-256ハッシュオブジェクトを生成 — 3.9以降のusedfips=TrueはFIPS承認アルゴリズムに制限

鍵付きハッシュ用の hmac.new() パラメータ:

パラメータ
説明
key
bytes
秘密鍵 — strではなくbytesでなければならない
msg
bytes | None
認証する初期メッセージ — デフォルトはNone、後でupdate()を呼び出し可能
digestmod
str | callable
ハッシュアルゴリズム:hashlib.sha256または文字列"sha256"を渡す

cryptographyライブラリ — 代替のSHA-256 API

cryptography パッケージはhazmatプリミティブを通じてSHA-256の異なるAPIを提供します。単純なハッシュが必要な場合はほとんど使用しません — hashlib の方がシンプルで外部依存がありません。しかし、プロジェクトがすでにTLS、X.509、または対称暗号化のために cryptography に依存している場合、そのハッシュAPIを使用することでライブラリを一本化し、一貫したエラーハンドリングが可能になります。

Python 3.9+ — cryptographyライブラリを使ったSHA-256
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend

digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(b"deployment-v4.2.1")
result = digest.finalize()  # 生の32バイト

print(result.hex())  # 64文字の16進数文字列
# a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db
警告:cryptographyライブラリには pip install cryptographyが必要です。ハッシュオブジェクトは使い捨てです: .finalize()を2回呼び出すと AlreadyFinalizedが発生します。ハッシュ状態を分岐させる必要がある場合はファイナライズ前に .copy()を使用してください。

ファイルとAPIレスポンスのデータをハッシュ

常によく出てくる2つのシナリオがあります:リリースアーティファクトを検証するためのディスク上のファイルのハッシュ化と、キャッシュキーとして使用するかWebhookを検証するためのHTTPレスポンスボディのハッシュ化です。

ファイル読み込み → SHA-256計算 → 比較

Python 3.9+ — エラーハンドリングを含むコンフィグバックアップのハッシュ
import hashlib
import sys

def hash_file_safe(filepath: str) -> str | None:
    """適切なエラーハンドリングを含むファイルのハッシュ計算。"""
    try:
        h = hashlib.sha256()
        with open(filepath, "rb") as f:
            for chunk in iter(lambda: f.read(16384), b""):
                h.update(chunk)
        return h.hexdigest()
    except FileNotFoundError:
        print(f"エラー: {filepath} が見つかりません", file=sys.stderr)
        return None
    except PermissionError:
        print(f"エラー: {filepath} の読み込み権限がありません", file=sys.stderr)
        return None

result = hash_file_safe("/etc/nginx/nginx.conf")
if result:
    print(f"SHA-256: {result}")

HTTPレスポンス → ボディをハッシュしてキャッシュキーを生成

Python 3.9+ — APIレスポンスのハッシュ
import hashlib
import urllib.request
import json

def fetch_and_hash(url: str) -> tuple[dict, str]:
    """APIからJSONを取得し、データとそのSHA-256ハッシュを返す。"""
    try:
        with urllib.request.urlopen(url, timeout=10) as resp:
            body = resp.read()
            content_hash = hashlib.sha256(body).hexdigest()
            data = json.loads(body)
            return data, content_hash
    except urllib.error.URLError as exc:
        raise ConnectionError(f"{url} の取得に失敗: {exc}") from exc

# レスポンス内容に基づくキャッシュキー
data, digest = fetch_and_hash("https://api.exchange.internal/v2/rates")
print(f"レスポンスハッシュ: {digest[:16]}...")
print(f"EUR/USD: {data.get('rates', {}).get('EUR', 'N/A')}")

手軽に試したい場合は、 ToolDeckのSHA-256ジェネレーター がブラウザ上で完全に動作します — コード不要です。

コマンドラインでのSHA-256ハッシュ

インシデント対応やデプロイ中にターミナルで手軽にハッシュが必要な場合があります。 Pythonの hashlib モジュールには組み込みのCLIサブコマンドがありません( python3 -m json.toolとは異なり)、 ただしワンライナーまたはシステムツールを使用できます。

bash — コマンドラインで文字列をハッシュ
# Pythonワンライナー
echo -n "deployment-v4.2.1" | python3 -c "import hashlib,sys; print(hashlib.sha256(sys.stdin.buffer.read()).hexdigest())"

# macOS / BSD
echo -n "deployment-v4.2.1" | shasum -a 256

# Linux (coreutils)
echo -n "deployment-v4.2.1" | sha256sum

# OpenSSL(クロスプラットフォーム)
echo -n "deployment-v4.2.1" | openssl dgst -sha256
bash — ファイルをハッシュ
# リリースtar ballをハッシュ
sha256sum release-v4.2.1.tar.gz
# または
openssl dgst -sha256 release-v4.2.1.tar.gz

# 既知のチェックサムに対して検証
echo "a8f5f167f44f4964e6c998dee827110c release-v4.2.1.tar.gz" | sha256sum -c -
# release-v4.2.1.tar.gz: OK
注意:コマンドラインで文字列をハッシュする際は常に echo -n(末尾の改行なし)を使用してください。単純な echo \n を付加し、ハッシュが変わります。PythonとシェルでハッシュのGit値が異なる原因の大半がこれです。

高パフォーマンスの代替案 — hashlibとOpenSSL、pycryptodome

CPythonでは、 hashlib.sha256() はすでにOpenSSLのC実装に委譲しているため高速です — 最新のハードウェアでは通常500 MB/s以上です。

SHA-256ハッシュがプロファイラに出てくる場合 — たとえばCIパイプラインで数千のファイルのチェックサムを計算したり、高スループットAPIゲートウェイで全リクエストボディをハッシュする場合 — 2つの選択肢があります: hashlib の呼び出しパターンを最適化するか、SHA-3やBLAKE2もカバーする統合暗号APIの pycryptodome に切り替えることです:

bash — pycryptodomeのインストール
pip install pycryptodome
Python 3.9+ — pycryptodomeを使ったSHA-256
from Crypto.Hash import SHA256

h = SHA256.new()
h.update(b"deployment-v4.2.1")
print(h.hexdigest())
# a8f5f167f44f4964e6c998dee827110c3f1de4d0280c68cba98cf70b4b5157db

高スループットの並列ファイルハッシュでは、チャンクサイズを大きくしてスレッドを使用することでPythonのオーバーヘッドを削減できます:

Python 3.9+ — hashlibを使った並列ファイルハッシュ
import hashlib
import os
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor

def hash_file(path: Path) -> tuple[str, str]:
    """単一ファイルをハッシュし(パス, 16進数ダイジェスト)を返す。"""
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(65536), b""):  # 64 KBチャンク
            h.update(chunk)
    return str(path), h.hexdigest()

def hash_directory(directory: str, pattern: str = "*.tar.gz") -> dict[str, str]:
    """スレッドを使って一致するファイルを並列にハッシュ。"""
    files = list(Path(directory).glob(pattern))
    results = {}
    with ThreadPoolExecutor(max_workers=os.cpu_count()) as pool:
        for path, digest in pool.map(hash_file, files):
            results[path] = digest
    return results

# リリースアーティファクトを並列にハッシュ
checksums = hash_directory("/var/releases", "*.tar.gz")
for path, digest in checksums.items():
    print(f"{digest}  {path}")

8 KBの代わりに64 KBチャンクを使用することでPythonからCへの呼び出し回数が8分の1になります。C レベルのハッシュ処理中はGILが解放されるため、スレッドが有効です — ボトルネックはCPUではなくディスクI/Oです。

ターミナルへのシンタックスハイライト表示

rich ライブラリは、大量のファイルを検証する際に、生の16進数が流れるのではなくファイルごとの合否ステータスを示すテーブルを表示したい場合に役立ちます。

bash — richのインストール
pip install rich
Python 3.9+ — richを使ったハッシュ検証の表示
import hashlib
from pathlib import Path
from rich.console import Console
from rich.table import Table

console = Console()

def hash_and_display(files: list[str], expected: dict[str, str]) -> None:
    """ファイルをハッシュし、カラーコードで検証結果を表示。"""
    table = Table(title="SHA-256 検証")
    table.add_column("ファイル", style="cyan")
    table.add_column("SHA-256", style="dim", max_width=20)
    table.add_column("ステータス")

    for filepath in files:
        h = hashlib.sha256()
        with open(filepath, "rb") as f:
            for chunk in iter(lambda: f.read(8192), b""):
                h.update(chunk)
        digest = h.hexdigest()

        name = Path(filepath).name
        status = "[green]✓ OK[/green]" if expected.get(name) == digest else "[red]✗ 不一致[/red]"
        table.add_row(name, f"{digest[:16]}...", status)

    console.print(table)

# 使用例
expected_checksums = {
    "api-gateway-v3.1.tar.gz": "a8f5f167f44f4964...",
    "worker-v3.1.tar.gz": "7d3f8c2a1b9e4f5d...",
}
hash_and_display(
    ["/var/releases/api-gateway-v3.1.tar.gz", "/var/releases/worker-v3.1.tar.gz"],
    expected_checksums,
)
注意:Richの出力はターミナル表示専用です。ログファイルやAPIレスポンスにANSIエスケープコードを書き込まないでください — console.print(data, highlight=False) でエスケープコードを除去するか、Console(file=open(...))でファイルにリダイレクトしてください。

大きなファイルの処理

チャンク化された.update() パターンはあらゆるサイズのファイルを一定のメモリ使用量で処理します。非常に大きなファイル(複数GBのディスクイメージ、データベースバックアップ)では、懸念事項がメモリからユーザーへのフィードバックに移ります — 500 MB/sで10 GBをハッシュするには20秒かかり、その間の沈黙は不安を生じさせます。

Python 3.9+ — 進捗報告付きの大きなファイルのハッシュ
import hashlib
import os

def sha256_with_progress(filepath: str) -> str:
    """進捗をstderrに報告しながら大きなファイルをハッシュ。"""
    file_size = os.path.getsize(filepath)
    h = hashlib.sha256()
    bytes_read = 0

    with open(filepath, "rb") as f:
        while chunk := f.read(1 << 20):  # 1 MBチャンク
            h.update(chunk)
            bytes_read += len(chunk)
            pct = (bytes_read / file_size) * 100
            print(f"\r  ハッシュ中: {pct:.1f}% ({bytes_read >> 20} MB / {file_size >> 20} MB)",
                  end="", flush=True)

    print()  # 進捗後の改行
    return h.hexdigest()

digest = sha256_with_progress("/mnt/backups/db-snapshot-2026-03.sql.gz")
print(f"SHA-256: {digest}")

NDJSON / JSON Lines — レコードごとに個別にハッシュ

Python 3.9+ — NDJSONストリームの各レコードをハッシュ
import hashlib
import json

def hash_ndjson_records(filepath: str) -> dict[str, str]:
    """重複排除のためNDJSONファイルの各JSONレコードをハッシュ。"""
    seen = {}
    with open(filepath, "r", encoding="utf-8") as f:
        for line_num, line in enumerate(f, 1):
            line = line.strip()
            if not line:
                continue
            try:
                record = json.loads(line)
                # ハッシュ前に正規化:決定的出力のためキーをソート
                canonical = json.dumps(record, sort_keys=True, separators=(",", ":"))
                digest = hashlib.sha256(canonical.encode("utf-8")).hexdigest()

                if digest in seen:
                    print(f"行 {line_num}: 行 {seen[digest]} の重複")
                else:
                    seen[digest] = line_num
            except json.JSONDecodeError:
                print(f"行 {line_num}: 無効なJSON、スキップしました")

    print(f"{line_num}行を処理、{len(seen)}件のユニークなレコード")
    return seen

hash_ndjson_records("telemetry-events-2026-03.ndjson")
注意:ファイルが50〜100 MBを超えたら、単純なhashlib.sha256(data)ワンショットからチャンク化された.update()ループに切り替えてください。 それ以下のサイズであればf.read()でファイル全体を読み込んでも問題ありません — メモリ使用量はファイルサイズとほぼ同等です。

よくある間違い

hashlib.sha256()にbytesではなくstrを渡す

問題: hashlib.sha256('text')はTypeError: Unicode-objects must be encoded before hashingを発生させます。この関数はstrではなくbytesを要求します。

解決策: 最初に文字列をエンコードしてください:hashlib.sha256('text'.encode('utf-8'))。ハードコードされた値にはb''リテラルを使用します。

Before · Python
After · Python
import hashlib
digest = hashlib.sha256("deployment-v4.2.1").hexdigest()
# TypeError: Unicode-objects must be encoded before hashing
import hashlib
digest = hashlib.sha256("deployment-v4.2.1".encode("utf-8")).hexdigest()
# 動作する — 64文字の16進数文字列を返す
署名検証にhmac.compare_digest()ではなく==を使う

問題: ==演算子は最初に不一致のバイトで短絡評価します。攻撃者はレスポンスタイムを計測して正しい署名を1バイトずつ推測できます。

解決策: セキュリティ上重要なすべての比較にhmac.compare_digest()を使用してください — 不一致の位置に関係なく一定時間で実行されます。

Before · Python
After · Python
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()
if received_sig == expected_sig:  # タイミング攻撃に脆弱
    process_webhook(body)
received_sig = request.headers["X-Signature"]
expected_sig = hmac.new(key, body, hashlib.sha256).hexdigest()
if hmac.compare_digest(received_sig, expected_sig):  # 定時間
    process_webhook(body)
生バイトではなく16進数文字列をBase64エンコードする

問題: base64.b64encode(digest.hexdigest().encode())は88文字の文字列を生成します — 期待される44文字の2倍です。Base64エンコードされたSHA-256を期待するAPIはこれを拒否します。

解決策: Base64エンコード前に.hexdigest()(16進数文字列)ではなく.digest()(生バイト)を呼び出してください。

Before · Python
After · Python
import hashlib, base64
hex_str = hashlib.sha256(data).hexdigest()
b64 = base64.b64encode(hex_str.encode())  # 88文字 — 誤り!
import hashlib, base64
raw = hashlib.sha256(data).digest()
b64 = base64.b64encode(raw)  # 44文字 — 正しい
ハッシュ前に大きなファイル全体をメモリに読み込む

問題: hashlib.sha256(open('large.iso', 'rb').read())はファイル全体をメモリに読み込みます。4 GBのファイルはハッシュ計算だけで4 GBのRAMを必要とします。

解決策: ループと.update()でチャンク単位に読み込んでください。ファイルサイズに関係なくメモリ使用量は一定です。

Before · Python
After · Python
import hashlib
# 4 GBのファイル全体をメモリに読み込む
digest = hashlib.sha256(open("disk.iso", "rb").read()).hexdigest()
import hashlib
h = hashlib.sha256()
with open("disk.iso", "rb") as f:
    for chunk in iter(lambda: f.read(8192), b""):
        h.update(chunk)
digest = h.hexdigest()  # 一定のメモリ使用量

hashlib vs hmac vs 代替案 — 比較一覧

メソッド
出力
鍵付き
速度
ファイルストリーミング
インストール
カスタム型
hashlib.sha256()
hex / bytes
高速 (C/OpenSSL)
✓ update()経由
不要 (stdlib)
手動encode()
hmac.new()
hex / bytes
高速 (C/OpenSSL)
✓ update()経由
不要 (stdlib)
手動encode()
hashlib.file_digest()
hex / bytes
高速 (ゼロコピー)
✓ (組み込み)
不要 (3.11+)
手動encode()
cryptography hashes.SHA256()
bytes
高速 (OpenSSL)
✓ update()経由
pip install
手動encode()
subprocess openssl dgst
hex string
✗ / ✓
低速 (forkあり)
✓ (OSレベル)
System openssl
手動encode()
pyhashcat / custom
様々
GPU加速
pip install
手動encode()

単純なハッシュ — チェックサム、キャッシュキー、コンテンツフィンガープリント — には hashlib.sha256() を使用してください。秘密鍵が必要になった瞬間(Webhook、API署名、トークン認証)は hmac.new() に切り替えてください。 cryptography ライブラリは、プロジェクトがすでに暗号化やTLSのために使用している場合のみ選択してください — hashlibがすでにOpenSSLに支援されているのに、ハッシュのためだけにC拡張の依存関係を追加するのは過剰です。

SHA-256は復号できるか — ハッシュと暗号化の違い

端的に言うと、できません。SHA-256は一方向関数です。このアルゴリズムは不可逆であるよう設計されています — 256ビットダイジェストから元の入力を再構築することはできません。これは実装上の制限ではなく、ハッシュ関数の数学的な特性です。256ビットの出力空間は天文学的に大きく(2256通りの値)、この関数は64回の圧縮ラウンドで情報を破棄します。

攻撃者は弱い入力(一般的なパスワード、短い文字列)に対してブルートフォースや辞書攻撃を試みることができますが、適切なエントロピーを持つ入力 — APIキー、ランダムトークン、ファイルコンテンツ — に対してSHA-256を逆算することは現在のハードウェアでは計算上不可能です。可逆変換が必要な場合は対称暗号化を使用してください:

Python 3.9+ — 暗号化とハッシュの比較
# ハッシュ — 一方向、元の値を復元不可
import hashlib
digest = hashlib.sha256(b"secret-config-value").hexdigest()
# digestから"secret-config-value"を取り出す方法はない

# 暗号化 — 双方向、鍵で復号可能
from cryptography.fernet import Fernet
key = Fernet.generate_key()
cipher = Fernet(key)
encrypted = cipher.encrypt(b"secret-config-value")
decrypted = cipher.decrypt(encrypted)
print(decrypted)  # b"secret-config-value" — 元の値が復元された

インストール不要で手軽に SHA-256ハッシュを生成したい場合は、オンラインツールがブラウザ上で完全に動作します。

PythonでSHA-256ハッシュ形式を検証する方法

有効なSHA-256の16進数ダイジェストはちょうど64文字の16進数(0-9、a-f、A-F)です。信頼できない入力を処理する前にクイック検証を行うことで、後続の処理での不明瞭なエラーを防ぎます。

Python 3.9+ — SHA-256形式の検証
import re

def is_sha256_hex(value: str) -> bool:
    """文字列がSHA-256 16進数ダイジェスト形式に一致するか確認。"""
    return bool(re.fullmatch(r"[a-fA-F0-9]{64}", value))

# テストケース
print(is_sha256_hex("e3b0c44298fc1c149afbf4c8996fb924"
                     "27ae41e4649b934ca495991b7852b855"))  # True — 空文字列のSHA-256
print(is_sha256_hex("e3b0c44298fc1c14"))                   # False — 短すぎる
print(is_sha256_hex("zzzz" * 16))                          # False — 無効な16進数文字
注意:これは形式のみを検証するものであり、特定の入力から計算されたハッシュかどうかは検証しません。64文字の16進数文字列が「本物の」SHA-256ダイジェストかランダムな16進数かを判別する方法はありません — SHA-256の出力は設計上ランダムデータと区別できません。

よくある質問

PythonでSHA-256文字列ハッシュを計算するには?

hashlib.sha256()にバイト列にエンコードした文字列を渡します。Pythonの文字列はUnicodeですが、ハッシュ関数は生のバイト列を対象とするため、事前に.encode("utf-8")を呼び出す必要があります。.hexdigest()メソッドが64文字の16進数文字列を返します。

Python
import hashlib

api_key = "sk_live_9f3a7b2e1d4c"
digest = hashlib.sha256(api_key.encode("utf-8")).hexdigest()
print(digest)
# e3b7c4a1f8d2...64文字の16進数

SHA-256ハッシュを元のテキストに復号できますか?

できません。SHA-256は一方向関数です — 任意長の入力を固定256ビット出力にマッピングし、その過程で情報を破棄します。数学的な逆関数は存在しません。攻撃者は弱い入力(短いパスワード、一般的な単語)に対してブルートフォースやレインボーテーブル攻撃を試みることができますが、十分なエントロピーを持つ入力に対してSHA-256を逆算することは計算上不可能です。可逆変換が必要な場合はハッシュではなく暗号化(AES-GCM、Fernet)を使用してください。

.digest()と.hexdigest()の違いは何ですか?

.digest()はハッシュの生の32バイトをbytesオブジェクトとして返します。.hexdigest()は同じデータを64文字の小文字16進数文字列としてエンコードして返します。バイナリ出力が必要な場合 — HMACへの入力、Base64エンコード、バイナリプロトコルへの書き込み — は.digest()を使用します。ログ記録、データベース保存、チェックサム比較で人間が読める文字列が必要な場合は.hexdigest()を使用します。

Python
import hashlib

h = hashlib.sha256(b"deployment-v4.2.1")
print(len(h.digest()))     # 32 (バイト)
print(len(h.hexdigest()))  # 64 (16進数文字)

PythonでファイルのSHA-256チェックサムを計算するには?

ファイルをバイナリモードで開き、.update()でチャンク単位にハッシャーに投入します。Python 3.11以降ではhashlib.file_digest()がさらにシンプルなAPIを提供します。大きなファイルでf.read()を呼び出すとファイル全体がメモリに読み込まれるため避けてください。

Python
import hashlib

def sha256_file(path: str) -> str:
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(8192), b""):
            h.update(chunk)
    return h.hexdigest()

print(sha256_file("release-v4.2.1.tar.gz"))

PythonでHMAC-SHA256署名を作成するには?

digestmodにhashlib.sha256を指定してhmacモジュールを使用します。秘密鍵とメッセージをbytesとして渡します。結果は整合性と真正性の両方を証明する鍵付きハッシュです — 受信者は同じ鍵を持っていないと検証できません。

Python
import hmac
import hashlib

secret = b"webhook_secret_9f3a"
payload = b'{"event":"payment.completed","amount":4999}'
signature = hmac.new(secret, payload, hashlib.sha256).hexdigest()
print(signature)  # 64文字の16進数HMAC

文字列が有効なSHA-256 16進数ダイジェストかどうかを検証するには?

SHA-256の16進数ダイジェストはちょうど64文字の16進数です。正規表現または長さと文字のチェックを使用します。正規表現のアプローチが最も読みやすいです。

Python
import re

def is_sha256(s: str) -> bool:
    return bool(re.fullmatch(r"[a-fA-F0-9]{64}", s))

print(is_sha256("e3b0c44298fc1c149afbf4c8996fb924"
                 "27ae41e4649b934ca495991b7852b855"))  # True
print(is_sha256("not-a-hash"))  # False

関連ツール

DV
Dmitri VolkovDevOps Engineer & Python Automation Specialist

Dmitri is a DevOps engineer who relies on Python as his primary scripting and automation language. He builds internal tooling, CI/CD pipelines, and infrastructure automation scripts that run in production across distributed teams. He writes about the Python standard library, subprocess management, file processing, encoding utilities, and the practical shell-adjacent Python that DevOps engineers use every day.

MS
Maria Santos技術レビュアー

Maria is a backend developer specialising in Python and API integration. She has broad experience with data pipelines, serialisation formats, and building reliable server-side services. She is an active member of the Python community and enjoys writing practical, example-driven guides that help developers solve real problems without unnecessary theory.