【実務・中級編】 楕円曲線デジタル署名アルゴリズム(ECDSA)のNonce漏洩リスク – 暗号理論・認証基盤 & エンドポイントセキュリティ防御ガイド

署名生成の「たった一度のミス」が命取り:ECDSAのNonce漏洩という悪夢

エンジニア諸君、日々コードを書いていて「乱数」を軽く見てはいないか?
「とりあえず rand() で回しておけばいいや」という安直な実装が、どれほど残酷な結末を招くか、今日は少しだけ現場の裏側の話をする。

暗号の世界で、楕円曲線デジタル署名アルゴリズム(ECDSA)は、軽量かつ堅牢な署名方式としてWebの標準になっている。だが、このアルゴリズムには、実装者が最も陥りやすい、そして一度踏み抜けば即死する「地雷」がある。それが Nonce(k値)の漏洩・再利用 だ。

1. なぜ「Nonceの再利用」で秘密鍵がバレるのか

ECDSAの署名生成式を思い出してほしい。署名 $(r, s)$ を作る際、秘密鍵 $d$ に対して、毎回異なるランダムな値 $k$(Nonce)を用いる。

  • $r = (k \cdot G)_x \pmod n$
  • $s = k^{-1}(H(m) + r \cdot d) \pmod n$

ここで、$k$ というのは「二度と使ってはいけない使い捨ての乱数」だ。もし、同じ秘密鍵を使って、2つの異なるメッセージに「同じ $k$」を使って署名してしまったらどうなるか。

攻撃者は $s_1$ と $s_2$ の差分をとるだけで、$k$ を導出し、そこから逆算して一瞬で秘密鍵 $d$ を特定できる。これは計算量的な仮定の話ではなく、単純な代数演算による証明可能なクラックだ。2010年のPlayStation 3の署名アルゴリズムが破られた事件は、まさにこの「乱数生成の欠陥」が原因だった。

2. 決定論的署名(RFC 6979)という「唯一の解」

「じゃあ、OSの強力な乱数生成器(CSPRNG)を使えばいいのか?」という声が聞こえるが、現場の運用において乱数に頼るのはリスクが大きすぎる。ハードウェアの不具合、VMのクローンによる状態同期、ライブラリのバグ……。予測不能な要因で乱数が衝突する可能性はゼロではない。

そこで登場するのが RFC 6979 だ。これは、乱数に頼るのではなく、「秘密鍵とメッセージ」から「k値を決定論的に導出する」という手法だ。

同じ入力(秘密鍵+メッセージ)からは常に同じ $k$ が生成されるため、乱数の衝突リスクを排除できる。現代の堅牢なアプリケーション開発において、ECDSAの実装は RFC 6979 準拠が「暗黙の掟」である。

3. 実践:Pythonで学ぶ「安全な署名」の実装

多くのライブラリは適切に使えば RFC 6979 をサポートしている。例えば、Pythonの ecdsa ライブラリを使う場合、どのように書くべきか見てみよう。

import ecdsa
import hashlib

# セキュアな秘密鍵の生成
sk = ecdsa.SigningKey.generate(curve=ecdsa.SECP256k1)

# 署名対象のデータ
message = b"Transaction ID: 0001"

# 【推奨】決定論的署名(RFC 6979)の実装
# ecdsaライブラリの sign メソッドは、デフォルトでRFC 6979をサポートしている
# 自前で乱数 k を生成して渡すような「危ないコード」は絶対に書かないこと
signature = sk.sign(
    message,
    hashfunc=hashlib.sha256,
    sigencode=ecdsa.util.sigencode_der
)

print(f"署名生成成功: {signature.hex()}")

# 検証(公開鍵を使って検証する)
vk = sk.verifying_key
assert vk.verify(signature, message, hashfunc=hashlib.sha256)
print("検証OK: 署名は正当です")

ここでのポイント:

  • ライブラリの sign メソッドに、わざわざ k を渡すようなインターフェースがあっても、絶対に触るな。
  • hashfunc を明示的に指定し、ライブラリが内部で RFC 6979 に基づいてセキュアに $k$ を導出する挙動に任せるのが、最も安全な設計だ。

4. 現場のセキュリティチーフからの教訓

最後に、運用において死守すべき鉄則を伝えておく。

1. 車輪の再発明はするな: 署名ロジックを自分で実装しようと考えるな。OpenSSLや、信頼された言語標準の暗号ライブラリのみを使え。
2. VM環境のクローンに注意: コンテナや仮想マシンをコピーして運用する場合、乱数生成器のシードが同期してしまうケースがある。これこそがNonce重複の最大の温床だ。
3. WAFで防げない: この脆弱性はアプリレイヤーのロジックにある。WAFでリクエストを弾くことはできない。コードレビュー時に「乱数の生成方法」を必ず指摘の対象に含めること。

「たかが一つの変数」と侮るなかれ。セキュリティとは、こうした小さな積み重ねの強固さで決まる。もし君のプロジェクトで、古いライブラリを使って独自にNonceを管理している箇所を見つけたら、今すぐリファクタリングを提案してほしい。それが君のエンジニアとしての価値を証明する。

現場からは以上だ。また何かあればいつでも聞きに来い。

コメント

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