なぜ今さら「PKCS#1 v1.5」を使うのか?暗号実装の死角を突く
現場でコードレビューをしていると、未だに「とりあえず動くから」という理由で、暗号化方式に RSAES-PKCS1-v1_5 を指定しているケースに出くわすことがある。はっきり言おう。その選択は、あなたのシステムを「攻撃者にとっての格好の獲物」に変える行為だ。
今日は、RSA暗号におけるパディングの歴史的経緯と、なぜ OAEP(Optimal Asymmetric Encryption Padding)が現代の標準であるべきなのか、現場の知見を交えて徹底解説する。
—
1. PKCS#1 v1.5 が孕む「致命的」な脆弱性:Bleichenbacher攻撃
RSA暗号は、平文をそのまま数学的に変換するのではなく、暗号化の前に「パディング」という処理を行う。このパディングのルールが PKCS#1 v1.5 だ。
この方式の最大の問題は、エラーハンドリングの差異によるサイドチャネル攻撃(Bleichenbacher攻撃)を許してしまう点にある。具体的には、サーバーが「復号に失敗した」という事実を、レスポンスの微妙な時間差やエラーメッセージの種類を通じて外部に漏らしてしまうことで、攻撃者は暗号文を何千回、何万回とサーバーに送りつける「オラクル(預言者)攻撃」が可能になる。
攻撃者は、サーバーが返すわずかな反応の違いから「今回の復号データはパディングが正しい形式だったか?」を推測し、最終的には秘密鍵を使わずに暗号文を解読してしまう。この攻撃は、20年以上前に発見されたにもかかわらず、現代のWebサーバーやTLS実装でも設定ミスによって繰り返し再現されている、まさに「ゾンビのような脆弱性」だ。
—
2. なぜ OAEP が「必須」なのか?
RSA-OAEP は、Feistelネットワークを用いた構造で、ランダムな値を組み込むことで「決定論的(同じ入力なら同じ暗号文になる)」というRSAの弱点を打ち消している。
OAEPの最大の強みは、「暗号文の改ざん」に対して極めて堅牢であることだ。たとえ攻撃者が暗号文の一部を書き換えたとしても、復号時にパディングチェックが失敗し、情報が漏洩する前に処理を止めるように設計されている。これは単なる規約ではなく、現代の暗号学における「防御的設計」の結晶だ。
—
3. 実践:Python でのセキュアな実装
OpenSSLライブラリ等を使って実装する場合、デフォルトで PKCS1_v1_5 を選択させないことが重要だ。Pythonの cryptography ライブラリを使用した、推奨される実装例を紹介する。
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding, rsa
# 1. 鍵ペアの生成 (実務では適切に管理された公開鍵/秘密鍵を使用)
private_key = rsa.generate_private_key(public_exponent=65537, key_size=2048)
public_key = private_key.public_key()
message = b"Top Secret Data"
# 2. 【推奨】OAEPによる暗号化
# MGF1というハッシュ関数を組み合わせるのが現在のベストプラクティス
ciphertext = public_key.encrypt(
message,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
# 3. 【推奨】OAEPによる復号
plaintext = private_key.decrypt(
ciphertext,
padding.OAEP(
mgf=padding.MGF1(algorithm=hashes.SHA256()),
algorithm=hashes.SHA256(),
label=None
)
)
print(f"復号成功: {plaintext.decode()}")
ポイント解説
padding.OAEPを明示:PKCS1v15という文字列をコードから抹殺すること。SHA256の採用: 昔のSHA1は既に衝突耐性に懸念がある。現代のシステムではSHA256以上をハッシュアルゴリズムとして採用せよ。
—
4. 運用上の鉄則:エラーハンドリングの均一化
コードでOAEPを使っていても、サーバーの設計が甘ければ攻撃を許す。以下のルールを徹底してほしい。
1. エラー内容を秘匿する: 復号に失敗した際、「パディングエラーです」や「鍵が違います」といった具体的な情報は絶対に返さない。全ての失敗に対して「Internal Server Error」や「Authentication Failed」といった一律のレスポンスを返すこと。
2. レスポンス時間を一定にする: 処理時間を計測されないよう、復号失敗時も成功時と同様のダミー処理を走らせてレスポンスまでの時間を平滑化する(Constant-time操作)。
3. 古いライブラリの排除: プロジェクト内で openssl の古いバージョンや、mcrypt のようなメンテされていないライブラリが使われていないか、grep で徹底的に洗い出せ。
—
結び:技術負債を「セキュリティの負債」にするな
「昔からのコードだから」という言い訳は、インシデント発生時には通用しない。暗号技術は日進月歩であり、昨日まで安全だったものが今日は脆弱性リストに載ることは珍しくない。
もしあなたのシステムで PKCS1 v1.5 を見つけたら、それは「緊急の技術負債」としてチケットを切るべきだ。OAEPへの移行は、単なる実装の変更ではなく、あなたのプロダクトが信頼を維持するための最低限の礼儀であると心得てほしい。
現場からは以上だ。君たちのコードが、セキュアな未来を支えることを期待している。
コメント