こんにちは!セキュリティの現場で日々、システムの守りを固めているエンジニアです。
新しいシステムを作るとき、ログイン画面のパスワードハッシュ化や、通信を安全にする暗号化(SSL/TLS)など、さまざまなセキュリティ対策を実装しますよね。でも、その強固な暗号の裏側で、「たったひとつの小さなミス」が原因で、金庫の鍵が丸見えになってしまうことがあるのをご存知でしょうか?
今回は、暗号の仕組みを支える隠れた主役「乱数生成器(CSPRNG)」と、そこに潜む「エントロピー不足」というちょっと怖い罠について、身近な例えを交えながら優しく紐解いていきたいと思います。一歩ずつ、一緒に学んでいきましょう!
—
1. 泥棒も予測できる「偽りのランダム」にご用心
いきなりですが、あなたの家の玄関の鍵を想像してみてください。世界でひとつだけの複雑な形をした鍵穴と、ピッキングが非常に難しいディンプルキーがあれば、泥棒も簡単には入れませんよね。
では、もしその鍵が「1から10までの数字を順番に並べただけのダイヤル錠」だったらどうでしょう? 泥棒は「1、2、3…」と試していけば、すぐに開けて侵入できてしまいます。
これと全く同じことが、デジタル世界でも起こります。暗号通信やパスワードのリセットトークンを作る際、私たちは「ランダムな値(乱数)」を大量に使います。この乱数がもし「予測可能な規則性」を持っていたらどうなるでしょうか? 攻撃者は次の鍵(乱数)を簡単に推測し、あなたの大切なデータをこっそり覗き見できるようになってしまうのです。
プログラミングにおける「普通の乱数」の罠
プログラムを書き始めたばかりの頃、ランダムな値が欲しいときは、次のような関数を使ったことがあるかもしれません。
- Pythonの
randomモジュール - JavaScriptの
Math.random()
実はこれ、プログラミングの勉強やゲームのサイコロ振るのには便利なんですが、「セキュリティの目的」には絶対に使ってはいけないものなんです。なぜなら、これらの関数は「疑似乱数生成器(PRNG)」と呼ばれ、過去の数字から次に何が出るのかが計算でバレてしまう仕組みになっているからなんですね。
—
2. 暗号のための最強の番人:CSPRNGと「エントロピー」
セキュリティの世界では、攻撃者に絶対に読まれない・予測されない、正真正銘のランダムが必要です。そこで登場するのが、CSPRNG(Cryptographically Secure Pseudo-Random Number Generator:暗号学的疑似乱数生成器)という、ちょっと噛みそうな名前の仕組みです。
CSPRNGは、普通のラン数生成器とは桁違いの頑丈さを持っています。例えるなら、風で舞い散る落ち葉の枚数や、雨粒の落ちるタイミングなど、絶対に人間には予測できない自然の力をエネルギー源にしています。
この「予測不可能な不確実さの度合い」を、セキュリティ用語でエントロピー(Entropy)と呼びます。
エントロピー不足ってどういう状態?
例えば、新しいサーバーを立ち上げたばかりの時や、起動した直後のシステムは、まだ外部からのアクセスも少なく、パソコンの内部が「とても静かな状態」にあります。
この状態のまま無理やり乱数を作ろうとすると、材料(エントロピー)が足りず、似たり寄ったりの偏った数字が生まれてしまいます。これが「エントロピー不足」です。
防犯カメラの例で言えば、真っ暗闇で何も見えない状態なのに、無理やり映像を記録しようとしてノイズだらけの映像になってしまうようなものです。材料不足で作られた鍵は、当然ペラペラの偽物になってしまい、簡単に破られてしまいます。
—
3. 実践!安全な乱数(CSPRNG)をコードで書いてみよう
それでは、実際の開発現場でどのように安全な乱数を取り扱えばよいのか、Pythonを例に見ていきましょう。
先ほど「使ってはいけない」と言った random ではなく、OSが提供する安全な乱数ソース(CSPRNG)を利用する secrets モジュールを使います。
Pythonでの実装例(パスワードリセット用トークンの生成)
import secrets
import string
def generate_secure_token(length=32):
"""
OSのエントロピーを利用した暗号学的に安全なランダムトークンを生成します。
パスワードリセットやセッションIDなど、推測されては困る場面で使用します。
"""
# 使用する文字種(アルファベット大文字・小文字、数字)を定義
alphabet = string.ascii_letters + string.digits
# secrets.choice を使うことで、CSPRNG(安全な乱数)による文字選択が行われます
secure_token = ''.join(secrets.choice(alphabet) for _ in range(length))
return secure_token
# 実行例の表示
if __name__ == "__main__":
token = generate_secure_token(32)
print(f"生成された安全なトークン: {token}")
このコードでは、OSの深部にあるランダムな動き(CPUの動作タイミングや割り込みなど)をかき集めた安全なプールから値を取り出しているため、攻撃者に推測されるリスクを極限まで低く抑えることができます。
—
4. Web開発やインフラ構築における注意点
新人のインフラエンジニアやバックエンドエンジニアが陥りがちなポイントとして、「仮想環境やコンテナ(Dockerなど)の初期起動時」があります。
新しくクラウド上にサーバー(EC2インスタンスなど)を立ち上げた直後、OSのカーネルがまだ十分なエントロピーを蓄えていないことがあります。その状態で、SSHの鍵ペアを自動生成したり、SSL証明書を急いで発行させたりすると、エントロピー不足のまま脆弱な暗号鍵が作られてしまう事故が過去に多発しました。
現場で役立つ対策のヒント
- OSの機能を信じる: 近年のモダンなOS(Linuxの
/dev/urandomや WindowsのCryptGenRandom/BCryptGenRandom)は非常に優秀にエントロピーを管理しています。基本的には言語標準の暗号用ライブラリ(Pythonのsecretsや Node.jsのcrypto.randomBytes()など)に任せるのが一番安全です。 - 無理なエントロピー追加ツールの乱用を避ける: 昔はエントロピーを強制的に増やすために
havegedなどのサードパーティ製デーモンを入れる手法が流行りましたが、現代のLinuxカーネル(v5.6以降など)では標準機能で十分に高速かつ安全に乱数が生成されるため、よほどの組み込み環境でなければ追加の仕組みは不要、むしろリスクになることもあります。
—
まとめ
いかがでしたでしょうか? 今回は暗号の土台を支える「乱数生成器(CSPRNG)」と「エントロピー」について紐解いてみました。
- 趣味やテスト用の
randomと、セキュリティ用のsecretsやcryptoをしっかり使い分けること。 - 予測可能な乱数は、泥棒にスペアキーを配っているようなものだと意識すること。
セキュリティの世界は一見すると難解な用語ばかりですが、一つひとつの原理を身近な例えに置き換えて理解していけば、決して怖くありません。今日からあなたの書くコードの中に、ぜひ「安全な乱数」を取り入れてみてくださいね。
それでは、また次回のセキュリティ解説でお会いしましょう!
コメント