セキュリティー関連のプログラミングまとめ(Python)

未分類

この記事のゴール

  • 高度な暗号化デジタル署名などの概念をざっくりつかむ
  • Pythonのcryptographyライブラリを使った実装方法を知る
  • 実践レベルで使えるサンプルコードに触れる

暗号の世界は奥が深くて、専門書を読んでいると眠くなってしまうこともありますが、コードを書きながら体感的に理解を深めるのが一番!ということでやってみましょう。

なぜ「高度なセキュリティプログラミング」が必要なの?

近年、ランサムウェアや情報漏えい、データ改ざんなど、セキュリティ事故が頻発していますよね。システムやアプリケーションを作る際には、「ただ動けば良い」のではなく、「安全に動作する」ことがかなり重要です。

「秘密の情報を誰にも盗み見されないようにしたい」とか「情報が改ざんされていないことを証明したい」といったニーズはどんなサービスにも存在します。そこで登場するのが暗号化デジタル署名鍵管理などの高度なセキュリティ技術です。

暗号化の基本:対称鍵暗号と公開鍵暗号

暗号化には大きく分けて2種類あります。

  1. 対称鍵暗号(共通鍵暗号):暗号化・復号化に同じ鍵を使う
  2. 公開鍵暗号(非対称鍵暗号):暗号化と復号化で違う鍵を使う(公開鍵 / 秘密鍵)

身近な例でいうと、AESは対称鍵暗号の代表格、RSAECDSAは公開鍵暗号の代表格として知られています。

対称鍵暗号(AES-GCMによる認証付き暗号)

対称鍵暗号はシンプルで高速な一方、鍵を安全に共有するのが難しいという課題があります。しかし、認証付き暗号(AES-GCMなど)を使えば、暗号化だけでなくデータの完全性(改ざんされていないこと)も同時にチェックできます。

# -----------------------------------------------------
# AES-GCMを使った暗号化&復号の実装例
# -----------------------------------------------------
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.backends import default_backend
import os

def encrypt_aes_gcm(plaintext: bytes, key: bytes) -> tuple:
    """
    AES-GCMでplaintextを暗号化し、(ciphertext, nonce, tag)を返却
    """
    # 96ビット(12バイト)のnonceをランダム生成
    nonce = os.urandom(12)
    
    # AES-GCMオブジェクトを生成
    aesgcm = Cipher(
        algorithms.AES(key),
        modes.GCM(nonce),
        backend=default_backend()
    ).encryptor()
    
    # 暗号化と同時に認証タグを計算
    ciphertext = aesgcm.update(plaintext) + aesgcm.finalize()
    tag = aesgcm.tag
    return ciphertext, nonce, tag

def decrypt_aes_gcm(ciphertext: bytes, nonce: bytes, tag: bytes, key: bytes) -> bytes:
    """
    AES-GCMで(ciphertext, nonce, tag)を復号化し、plaintextを返却
    """
    # 復号器を初期化
    aesgcm = Cipher(
        algorithms.AES(key),
        modes.GCM(nonce, tag),
        backend=default_backend()
    ).decryptor()
    
    # 復号(ここでtagもチェックされる)
    plaintext = aesgcm.update(ciphertext) + aesgcm.finalize()
    return plaintext

if __name__ == "__main__":
    key = os.urandom(32)  # AES-256用の256ビット(32バイト)鍵をランダム生成
    original_text = b"Top Secret Data"

    # 暗号化
    ct, nonce, tag = encrypt_aes_gcm(original_text, key)
    print("暗号文:", ct.hex())
    print("Nonce:", nonce.hex())
    print("Tag:", tag.hex())

    # 復号
    decrypted_text = decrypt_aes_gcm(ct, nonce, tag, key)
    print("復号結果:", decrypted_text.decode("utf-8"))

上記のサンプルでは、AES-GCMを使って「暗号化+改ざん検知」を同時に実現しています。tag(認証タグ)のおかげで、「誰かが暗号文を改ざんしていないか?」もチェックできるんですね。

公開鍵暗号(RSA)

一方で公開鍵暗号は、暗号化に公開鍵、復号化に秘密鍵を使用します。公開鍵を広く配布しておけば、誰でもデータを暗号化できるけど、復号は秘密鍵を持っている人だけが可能、という仕組みです。
こんな感じで鍵交換を安全に行ったり、デジタル署名を生成したりという用途に使われます。

デジタル署名と認証

「このデータ、誰が書いたの?」「本当に本人が書いたもの?」という認証や、改ざん検出を実現するために使われるのがデジタル署名です。
公開鍵暗号方式の代表であるRSAやECDSAで署名と検証を行います。

RSAでデジタル署名をする例

cryptographyライブラリを使えば、Pythonでもサクッと実装可能です。

# -----------------------------------------------------
# RSA署名と検証のサンプル
# -----------------------------------------------------
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import rsa, padding
from cryptography.hazmat.primitives import hashes
from cryptography.exceptions import InvalidSignature

def generate_rsa_keypair(key_size=2048):
    """RSAの鍵ペア(公開鍵・秘密鍵)を生成して返す"""
    private_key = rsa.generate_private_key(
        public_exponent=65537,
        key_size=key_size,
        backend=default_backend()
    )
    public_key = private_key.public_key()
    return private_key, public_key

def sign_data(private_key, message: bytes) -> bytes:
    """秘密鍵でmessageに署名して返す"""
    signature = private_key.sign(
        message,
        padding.PSS(
            mgf=padding.MGF1(hashes.SHA256()),
            salt_length=padding.PSS.MAX_LENGTH
        ),
        hashes.SHA256()
    )
    return signature

def verify_signature(public_key, message: bytes, signature: bytes) -> bool:
    """公開鍵で署名を検証。正しければTrue、誤りがあればFalse"""
    try:
        public_key.verify(
            signature,
            message,
            padding.PSS(
                mgf=padding.MGF1(hashes.SHA256()),
                salt_length=padding.PSS.MAX_LENGTH
            ),
            hashes.SHA256()
        )
        return True
    except InvalidSignature:
        return False

if __name__ == "__main__":
    # 鍵ペアを生成
    private_key, public_key = generate_rsa_keypair()
    
    # 署名対象メッセージ
    data = b"Hello, This is a message to be signed."

    # 署名を生成
    signature = sign_data(private_key, data)
    print("署名:", signature.hex())

    # 署名を検証
    result = verify_signature(public_key, data, signature)
    print("署名検証結果:", result)

この例では、sign_data()関数で秘密鍵を使ってメッセージに署名し、verify_signature()関数で公開鍵を使って検証しています。

ポイントは、この署名が署名者(秘密鍵保持者)の正当性メッセージの改ざん検知を兼ね備えていること。もしメッセージが1バイトでも変わっていたら検証は失敗するし、署名者が秘密鍵を持っていなければこの署名は作れないというわけです。

鍵管理の重要性

暗号化もデジタル署名も、最終的に「鍵をどう守るか」が大問題になります。

  • ハードウェアセキュリティモジュール(HSM)の利用
  • クラウドKMS(Key Management Service)の活用
  • オフラインでの厳格な保管やアクセス権限の分割(秘密鍵は複数人の承認がないと取り出せない、など)

など、運用面での工夫がとても大切。プログラムをしっかり書いても、鍵が漏れたらすべて水の泡です…。

実行結果のイメージ

ここまでのサンプルを実行すると、こんな感じでコンソールに出力されるはずです。

暗号文: d4a34adac3...
Nonce: 9f28ab1379...
Tag: 49274fda9c...
復号結果: Top Secret Data

署名: 3456f91cb6...
署名検証結果: True

「暗号がきちんとできた」「署名が検証できた」という結果が目で見えると、ちょっとわくわくしませんか?(私だけ…?)

応用編:ハッシュ・HMAC・EC暗号

今回紹介したのはセキュリティプログラミングのごく一部。暗号・署名・鍵管理だけでも、ハッシュ関数(SHA-256など)やメッセージ認証コード(HMAC)、楕円曲線暗号(ECDSA, ECDH)など、さまざまなテクノロジーがあります。
さらに奥へ進むと、Quantum-safe暗号(量子コンピュータに耐性を持つ暗号)なんて話もあったりして、まさに底なし沼…。ですが、現実的な開発現場では「何が必要なのか」「どうやって安全に運用するのか」を見極める力が重要ですね。

まとめ

  • 暗号の世界では対称鍵暗号公開鍵暗号を使い分ける
  • デジタル署名で改ざん検知真正性の保証ができる
  • 鍵管理がセキュリティの要。運用体制の整備も忘れずに
  • Pythonのcryptographyライブラリを使えば、かなり簡単に実装できる

今回はざっくりとした概要と基本的なコード例を紹介しました。実際には、プロトコル設計や鍵管理、攻撃シナリオへの対策など、もっと複雑な問題も山積み。でも、まずはこうした基本的な仕組みを理解していると、**「あ、こうやってデータを暗号化してるんだ」「この署名はこういう役割なんだ」**っていう感覚が掴めるようになりますよ。

もし、もっと詳しく知りたい!という方は、NISTのSP800シリーズRFCなど、公式ドキュメントもぜひ覗いてみてください。専門用語が大量に出てきて最初はびっくりするかもしれませんが、その分やりがいもバッチリです。

みなさんもぜひ、手を動かしながらセキュリティプログラミングを楽しんでくださいね。
それでは、また別の記事でお会いしましょう!

コメント