DLPの情報識別方式
識別条件、読める範囲、宛先別の制御と監査記録を分けて評価します。
説明
DLPは機微情報を識別し、持出しなどの操作に規則を適用します。文字列の形式、登録データとの一致、付与されたラベルは異なる根拠であり、どれか一つがすべての内容を識別するわけではありません。
EMPから始まる架空のIDを使い、正規表現による検査と登録値との照合を比較します。最後にAES-GCMで暗号化したデータを使い、鍵の有無で検査できる範囲がどう変わるかを確認します。
識別条件、読める範囲、宛先別の制御と監査記録を分けて評価します。
1本文と登録値を開く
EMP-0042とEMP-0071だけを登録済みとします。
from sp10 import *
print(TEXT)
print(sorted本文と登録値を開く(KNOWN))本文には登録値、未登録の同形式値、長い文字列に埋まった値があります。これらを同じ条件で検査します。
2広いパターンの一致を確認する
境界条件を持たない正規表現です。
print(re.findall広いパターンの一致を確認する(r'EMP-[0-9]{4}',TEXT))埋込み文字列の途中もEMP-0042として一致します。形式が同じだけでは独立したIDの出現とは限りません。
3前後の英数字を除外する
前後に英大文字や数字がある一致を除くパターンです。
print(PATTERN.pattern)
found=PATTERN.findall前後の英数字を除外する(TEXT)
print(found)EMP-0042とEMP-9999の2件が残ります。この境界は教材のID書式に合わせた条件で、日本語や任意の識別子すべての区切り規則ではありません。
4登録データとの完全一致を加える
形式一致から登録済みの値だけを抽出します。
print([x for x in found if x in KNOWN登録データとの完全一致を加える])EMP-0042だけです。登録値照合は未知の値を見つける方法ではありません。登録範囲や更新時点も検査範囲に影響します。
5ラベルと内容検査の結果を並べる
ラベルは固定の文書属性として与えます。
files=[('a.txt','internal',TEXT),('b.txt','confidential','public sample')]
for name,label,body in files:
print(name,label,[x for x in PATTERN.findallラベルと内容検査の結果を並べる(body) if x in KNOWN])aはinternalでも登録値を含み、bはconfidentialでも今回のIDはありません。ラベルの付与方法と内容検査の根拠を混ぜずに扱います。
6暗号化した内容への文字列検索を試す
公開試験鍵と固定nonceで一つの試料を作成します。
aes=AESGCM(PUBLIC_KEY)
nonce=bytes(range(12))
cipher=aes.encrypt暗号化した内容への文字列検索を試す(nonce,TEXT.encode(),b'file:a')
print(len(TEXT.encode()),len(cipher))
print(b'EMP-0042' in cipher)暗号文には平文のIDがそのまま現れません。固定nonceはこの単一の再現試料用で、同じ鍵による別の暗号化に再利用しません。
7鍵と認証情報を使って検査可能な値へ戻す
同じ鍵、nonce、付加認証データで復号します。
plain=aes.decrypt鍵と認証情報を使って検査可能な値へ戻す(nonce,cipher,b'file:a').decode()
print(plain==TEXT)
print([x for x in PATTERN.findall(plain) if x in KNOWN])元の本文と一致し、登録値が見つかります。暗号文しか見えない検査地点と、復号できる地点では識別できる範囲が変わります。
8認証に失敗したデータを正常な空文書にしない
別のファイル用の認証情報で復号します。
try:
aes.decrypt認証に失敗したデータを正常な空文書にしない(nonce,cipher,b'file:b')
except InvalidTag as e:
print(type(e).__name__)InvalidTagです。検査不能を「機微情報なし」に変換すると、読めなかったデータを許可してしまいます。
まとめ
形式一致と登録データ一致は別の識別条件です。
ラベルと内容検査の根拠を確認します。
暗号化などによる検査不能を陰性と扱いません。
操作履歴と解説を振り返れます。
Python 3.15 / AES-GCM / SQLiteの配送待ちモデル
from sp10 import *
print(TEXT)
print(sorted(KNOWN))owner=EMP-0042; sample=EMP-9999; embedded=XEMP-0042Z ['EMP-0042', 'EMP-0071']
print(re.findall(r'EMP-[0-9]{4}',TEXT))['EMP-0042', 'EMP-9999', 'EMP-0042']
print(PATTERN.pattern)
found=PATTERN.findall(TEXT)
print(found)(?<![A-Z0-9])EMP-[0-9]{4}(?![A-Z0-9])
['EMP-0042', 'EMP-9999']print([x for x in found if x in KNOWN])['EMP-0042']
files=[('a.txt','internal',TEXT),('b.txt','confidential','public sample')]
for name,label,body in files:
print(name,label,[x for x in PATTERN.findall(body) if x in KNOWN])a.txt internal ['EMP-0042'] b.txt confidential []
aes=AESGCM(PUBLIC_KEY)
nonce=bytes(range(12))
cipher=aes.encrypt(nonce,TEXT.encode(),b'file:a')
print(len(TEXT.encode()),len(cipher))
print(b'EMP-0042' in cipher)52 68 False
plain=aes.decrypt(nonce,cipher,b'file:a').decode()
print(plain==TEXT)
print([x for x in PATTERN.findall(plain) if x in KNOWN])True ['EMP-0042']
try:
aes.decrypt(nonce,cipher,b'file:b')
except InvalidTag as e:
print(type(e).__name__)InvalidTag
演習完了