RSAの「処理時間」が命取りになる:サイドチャネル攻撃の深淵と防御の極意
現場でコードを書いていると、「暗号化ライブラリを使っているから安全だ」と信じ込みたくなる気持ちはよくわかる。だが、CISSPとして数々のインシデントを見てきた経験から言わせてもらうと、暗号アルゴリズムそのものの脆弱性よりも、その「実装」の隙を突かれることの方が圧倒的に多い。
今日は、RSA暗号の心臓部を蝕む「タイミング攻撃(Timing Attack)」について話そう。これは計算の「正しさ」ではなく、計算に費やした「時間」を盗み見る、非常にエレガントで残酷な攻撃だ。
—
なぜ「計算時間」で鍵がバレるのか?
RSA暗号の秘密鍵操作には、「べき乗剰余演算(Modular Exponentiation)」が使われる。ここでよく使われるのが「バイナリ法(Square-and-Multiply)」というアルゴリズムだ。
# 脆弱なべき乗剰余のイメージ
def power_mod(base, exponent, modulus):
result = 1
for bit in bin(exponent)[2:]:
result = (result * result) % modulus # 常に実行
if bit == '1':
result = (result * base) % modulus # 「1」の時だけ追加で計算!
return result
見ての通り、秘密鍵のビットが 1 か 0 かによって、if 文の中身(乗算)を実行するかどうかが変わる。攻撃者は、この微小な計算時間の差を数千〜数万回計測して統計的に処理することで、秘密鍵のビットを1つずつ剥がしていく。これがタイミング攻撃の基本原理だ。
ネットワーク越しでも、統計的なノイズを排除する手法(Kocherのアルゴリズムなど)を使えば、昨今の高速なサーバー環境でも秘密鍵の抽出は現実的な脅威になり得る。
—
「定数時間」で実装する:脆弱性を断つ唯一の解
この攻撃を防ぐ唯一の確実な方法は、「秘密鍵に依存する処理時間を一定にする(Constant Time Implementation)」ことだ。条件分岐によって処理時間を変えてはならない。
幸いなことに、現代の開発者が自前でRSAの実装を書く機会はほとんどないはずだ。もし書いているなら、それは即座にやめるべきだ。しかし、ライブラリの選定や設定においては、この概念を知っておく必要がある。
Python: cryptography ライブラリを用いたセキュアな署名
PythonでRSAを扱うなら、内部で定数時間処理を強く意識した cryptography ライブラリ一択だ。
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.hazmat.primitives import serialization
# 秘密鍵を安全にロード
with open("private_key.pem", "rb") as key_file:
private_key = serialization.load_pem_private_key(key_file, password=None)
# 署名生成:ライブラリ内部でサイドチャネル対策が施されている
# 重要なのは、paddingに PSS を使用し、処理時間の揺らぎを排除した実装を利用すること
signature = private_key.sign(
b"message_to_sign",
padding.PSS(
mgf=padding.MGF1(hashes.SHA256()),
salt_length=padding.PSS.MAX_LENGTH
),
hashes.SHA256()
)
インフラエンジニアが取るべき防御策
アプリケーション層だけでなく、インフラ側でも「観測」を妨害することは重要だ。
1. Jitter(ジッター)の挿入: 処理時間にランダムな遅延を挿入する手法があるが、これは「対症療法」に近い。統計処理でノイズを除去される可能性があるため、あくまで補助的な防御だ。
2. ハードウェアセキュリティモジュール(HSM)の利用: クラウドを利用しているなら、AWS CloudHSMやAzure Dedicated HSMを活用すべきだ。秘密鍵は物理的に保護された専用ハードウェア内で処理されるため、OSレベルのタイミング攻撃は無効化できる。
3. レートリミットの厳格化: 攻撃者は大量の試行を必要とする。WAFやAPI Gatewayで、署名生成エンドポイントへのリクエスト頻度を物理的に制限することは、攻撃の試行回数を稼げなくするという意味で非常に有効だ。
Nginxでのレートリミット例 (nginx.conf)
# 署名生成用エンドポイントへの攻撃を防ぐため、IPごとのリクエストを制限
limit_req_zone $binary_remote_addr zone=sign_limit:10m rate=1r/s;
server {
location /api/v1/sign {
limit_req zone=sign_limit burst=5 nodelay;
proxy_pass http://backend_server;
}
}
—
まとめ:エンジニアとしての矜持
サイドチャネル攻撃は、数学的に完璧な暗号アルゴリズムという「聖域」の足元をすくう攻撃だ。「自分の書いたコードは数学的に正しい」という慢心は、セキュリティの世界では最も危険なフラグになる。
- 鉄則1: RSAの実装は絶対に自作しない。定数時間実装が保証されたライブラリを使うこと。
- 鉄則2: 秘密鍵にアクセスするAPIにはレートリミットをかけ、統計的な解析を困難にすること。
- 鉄則3: クラウド環境であれば、可能な限りマネージドHSMを活用し、鍵そのものがアプリケーションのメモリ空間に永続的に存在しない設計を心がけること。
セキュリティは「完成」ではなく「状態」だ。今日紹介した攻撃手法と防御策を、ぜひ君のシステムの設計レビューやコードベースの確認に役立ててほしい。君が書くコードが、誰かの資産を守る「最後の砦」であることを忘れないでくれ。
コメント