付録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