付録D: セキュリティベストプラクティス

ゲノムデータの安全な取り扱い

🧪 概念例(擬似コード)

import base64
import hashlib
import hmac
from cryptography.fernet import Fernet

class SecureGenomicDataHandler:
    """ゲノムデータの安全な取り扱い"""
    
    def __init__(self, data_encryption_key, pseudonymization_secret):
        """
        引数:
          - data_encryption_key: 対称暗号鍵(Fernetの形式, bytes または UTF-8 文字列)。実運用ではKMS/Vault等で生成・保管し、
            アプリケーションには必要最小限の権限で安全に注入する。
          - pseudonymization_secret: 仮名化用の秘密情報(pepper, bytes または UTF-8 文字列)。ハッシュのみ(秘密なし)は推測され得るため、
            小さいID空間や辞書攻撃が成立するケースでは避ける。
        """
        if isinstance(data_encryption_key, str):
            data_encryption_key = data_encryption_key.encode("utf-8")
        if isinstance(pseudonymization_secret, str):
            pseudonymization_secret = pseudonymization_secret.encode("utf-8")
        self.cipher = Fernet(data_encryption_key)
        self._pseudonymization_secret = pseudonymization_secret
    
    def pseudonymize_sample_id(self, original_id, length=16):
        """
        サンプルIDの仮名化(再識別キーを保持する前提)。
        匿名化とは異なり、運用・法務要件に応じて再識別可能性を管理する。
        """
        digest = hmac.new(
            self._pseudonymization_secret,
            original_id.encode("utf-8"),
            hashlib.sha256,
        ).digest()
        # URL安全な形式にして短縮(衝突リスクは length と入力規模で評価)
        return base64.urlsafe_b64encode(digest).decode("ascii")[:length]
    
    def encrypt_sensitive_data(self, data):
        """機密データの暗号化"""
        if isinstance(data, str):
            data = data.encode("utf-8")
        
        encrypted = self.cipher.encrypt(data)
        return encrypted

匿名化/仮名化/暗号化の整理(最低限):

  • 匿名化: 個人を合理的に再識別できない状態にする(一般に要件が厳しい)
  • 仮名化: 秘密情報(対応表/鍵)を適切に分離・管理し、識別子を別名に置換する
  • 暗号化: 鍵があれば復号可能。鍵管理が不適切だと保護にならない

注意点(誤運用の典型):

  • SHA-256等の「秘密なしハッシュ」は、入力空間が小さい場合(例: 連番ID)や辞書が作れる場合に推測され得るため、 匿名化として扱わない。必要に応じて、HMAC等の「秘密あり」方式を用い、秘密情報はKMS/Vault等で管理する。
  • 「安全な削除(上書き)」はストレージ種別(SSD/スナップショット/オブジェクトストレージ等)に依存し万能ではない。 実運用では暗号化(at rest)+ 鍵の破棄/ローテーション、アクセス制御、保持期間ポリシーを優先する。

アクセス制御の実装

🧪 概念例(擬似コード)

from functools import wraps
import jwt
from datetime import datetime, timedelta

class AccessControl:
    """ゲノムデータへのアクセス制御"""
    
    def __init__(self, secret_key):
        self.secret_key = secret_key
        
    def generate_token(self, user_id, permissions):
        """アクセストークンの生成"""
        payload = {
            'user_id': user_id,
            'permissions': permissions,
            'exp': datetime.utcnow() + timedelta(hours=24)
        }
        
        token = jwt.encode(payload, self.secret_key, algorithm='HS256')
        return token
    
    def verify_permission(self, required_permission):
        """権限検証デコレータ"""
        def decorator(func):
            @wraps(func)
            def wrapper(self, token, *args, **kwargs):
                try:
                    payload = jwt.decode(
                        token, 
                        self.secret_key, 
                        algorithms=['HS256']
                    )
                    
                    if required_permission not in payload['permissions']:
                        raise PermissionError("Insufficient permissions")
                        
                    return func(self, *args, **kwargs)
                    
                except jwt.ExpiredSignatureError:
                    raise ValueError("Token expired")
                except jwt.InvalidTokenError:
                    raise ValueError("Invalid token")
                    
            return wrapper
        return decorator