付録D: セキュリティベストプラクティス
セキュリティ確認の基本手順
セキュリティ上の問題は、コード例だけでは完結しません。ゲノムデータや研究データを扱う場合は、インシデント対応と平時運用の両方で 症状 / 原因 / 確認コマンド / 対処 / 再発防止 を分けて記録します。
- 症状: 想定外のユーザーがデータを読める、鍵やtokenがリポジトリに混入する、監査ログが残っていない、暗号化設定が環境ごとに異なる。
- 原因: 最小権限の未適用、秘密情報の配布経路不備、設定差分、ログ保管期間不足、バックアップや一時ファイルの見落とし。
- 確認コマンド:
git status --short,git log -- <path>,grep -RE "PRIVATE KEY|TOKEN|PASSWORD" ., クラウドIAM/Secrets/監査ログの一覧確認を行う。 - 対処: 秘密情報の失効とローテーション、権限の縮小、監査ログの有効化、暗号化鍵のKMS/Vault管理、不要なコピーの削除を行う。
- 再発防止: secret scan、アクセス権レビュー、鍵ローテーション手順、バックアップ復旧訓練、監査ログ確認を定期運用に入れる。
ゲノムデータの安全な取り扱い
- 症状: サンプルIDや機微データが平文で保存される、再識別キーと解析データが同じ場所に置かれる、暗号化鍵の所在が不明になる。
- 原因: 仮名化、匿名化、暗号化、鍵管理、アクセス制御の責務を分離していない。
- 確認コマンド:
grep -RE "sample_id|patient|subject" <data-dir>,find <data-dir> -maxdepth 2 -type f, KMS/Vault/IAM設定の一覧で保存場所と権限を確認する。 - 対処: HMAC等の秘密あり仮名化、暗号化 at rest、鍵の分離管理、不要コピー削除、保持期間ポリシーを適用する。
- 再発防止: データ分類、鍵管理手順、再識別キーの保管場所、監査ログ、削除・ローテーション手順を運用文書に残す。
🧪 概念例(安全な取り扱いの説明例)
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()
# Base64URL形式にしてパディングを除去してから短縮(衝突リスクは length と入力規模で評価)
token = base64.urlsafe_b64encode(digest).decode("ascii").rstrip("=")
return token[: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)+ 鍵の破棄/ローテーション、アクセス制御、保持期間ポリシーを優先する。
アクセス制御の実装
- 症状: 権限不足のユーザーがデータへアクセスできる、期限切れtokenが受理される、権限変更がログに残らない。
- 原因: 認可チェック漏れ、token検証条件不足、ロール設計の曖昧さ、監査ログ未設定。
- 確認コマンド: 代表ユーザーごとの権限一覧、token expiry、APIログ、失敗時ログ、監査ログを確認する。
- 対処: deny-by-default、最小権限、短い有効期限、権限変更の承認フロー、認可テストを導入する。
- 再発防止: 定期的なアクセス権レビュー、退職・異動時の権限剥奪、監査ログの保管期間、権限テストをCIや運用手順に入れる。
🧪 概念例(アクセス制御の説明例)
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
| ← 前へ: パフォーマンス最適化 | 目次に戻る | 次へ: コード例集 → |