証拠データの保全とハッシュ照合
ws7のSecurity記録を対象に、取得範囲、ファイルのコピー、ハッシュ、作業コピーへの追記を順に確認します。取得元、取得時刻、照合値を残し、原本と分析用ファイルを分けて扱う方法を学びます。
説明
調査では、対象を取得してよい範囲と、取得後に内容が変わっていないことを確認します。SHA-256はバイト列の同一性を調べるための値で、記録の内容が真実であることや、取得元の正当性まで保証するものではありません。
ws7のSecurity記録を対象に、取得範囲、ファイルのコピー、ハッシュ、作業コピーへの追記を順に確認します。取得元、取得時刻、照合値を残し、原本と分析用ファイルを分けて扱う方法を学びます。
1取得してよい対象を確認する
許可一覧のauthorizedは1が許可、0が対象外です。取得する前に対象の名前を照合します。
from oplab import *
db=connect(1)
show(db,"SELECT * FROM scope取得してよい対象を確認する")security.jsonlだけが取得対象です。同じ端末にある別の資料も自動的に許可されるわけではありません。
2取得済み試料の内容を開く
短いJSON Lines形式の試料を作業領域へ置きます。末尾の改行もファイルのバイト列に含まれます。
import tempfile, pathlib, hashlib, shutil
lab=tempfile.TemporaryDirectory()
root=pathlib.Path(lab.name)
original=root/"evidence.jsonl"
print(original.write_bytes取得済み試料の内容を開く(b'{"id":"e1","event":4624}\n'))
print(original.read_text(),end="")1行のイベント記録です。表示上の文字だけでなく、改行を含む実際のバイト列を照合します。
3原本の照合値を記録する
取得済みファイルをSHA-256へ渡します。記録した照合値は、後で同じファイルを読み直す際の比較対象です。
digest=hashlib.sha256原本の照合値を記録する(original.read_bytes()).hexdigest()
manifest={"source":"ws7/Security","acquired":"2026-01-07T01:10:00Z","sha256":digest}
print(json.dumps(manifest,sort_keys=True))取得元と取得時刻は取得記録から付与する情報です。ハッシュだけから端末名や取得時刻を復元することはできません。
4コピー先のバイト列を照合する
分析用ファイルを複製してから、複製先の内容を改めて読みます。
copy=root/"working.jsonl"
_ = shutil.copyfileコピー先のバイト列を照合する(original,copy)
print(hashlib.sha256(copy.read_bytes()).hexdigest()==digest)Trueは原本と同じ照合値だったことを示します。ファイル名や保存先が変わっても内容が同じなら照合できます。
5作業コピーの変更を検出する
分析メモを作業コピーに追記した場合を試します。JSON Linesのイベントとして保存するのではなく、差異が検出できるかを見る操作です。
with copy.open("ab") as f: _=f.write(b"analysis note\n")
print(hashlib.sha256(copy.read_bytes作業コピーの変更を検出する()).hexdigest()==digest)
print(hashlib.sha256(original.read_bytes()).hexdigest()==digest)コピーはFalse、原本はTrueです。加工済み資料のハッシュを原本の照合値として上書きすると、変更の経緯が分からなくなります。
6変更前後の記録を対応付ける
元の照合値と加工後の照合値を別の項目に残します。実務では操作者と操作時刻も記録します。
record変更前後の記録を対応付ける={"source_sha256":digest,"working_sha256":hashlib.sha256(copy.read_bytes()).hexdigest(),"operation":"append analysis note"}
print(json.dumps(record,sort_keys=True))
lab.cleanup()原本、作業コピー、操作記録の対応が残りました。ハッシュ照合と取得権限の確認は目的が異なり、どちらも必要です。
まとめ
取得範囲を確認し、原本の照合値を保存したうえで、コピーと加工後の資料を別に管理します。
操作履歴と解説を振り返れます。
Python 3.15 / tempfile・shutil・SHA-256
from oplab import *
db=connect(1)
show(db,"SELECT * FROM scope")host | item | authorized ws7 | security.jsonl | 1 ws7 | browser-private.db | 0
import tempfile, pathlib, hashlib, shutil
lab=tempfile.TemporaryDirectory()
root=pathlib.Path(lab.name)
original=root/"evidence.jsonl"
print(original.write_bytes(b'{"id":"e1","event":4624}\n'))
print(original.read_text(),end="")25
{"id":"e1","event":4624}digest=hashlib.sha256(original.read_bytes()).hexdigest()
manifest={"source":"ws7/Security","acquired":"2026-01-07T01:10:00Z","sha256":digest}
print(json.dumps(manifest,sort_keys=True)){"acquired": "2026-01-07T01:10:00Z", "sha256": "48042d70c5edad6ce571fac57a3778e96a6f2daf68f5775afa2168e6cc86d4f1", "source": "ws7/Security"}copy=root/"working.jsonl"
_ = shutil.copyfile(original,copy)
print(hashlib.sha256(copy.read_bytes()).hexdigest()==digest)True
with copy.open("ab") as f: _=f.write(b"analysis note\n")
print(hashlib.sha256(copy.read_bytes()).hexdigest()==digest)
print(hashlib.sha256(original.read_bytes()).hexdigest()==digest)False True
record={"source_sha256":digest,"working_sha256":hashlib.sha256(copy.read_bytes()).hexdigest(),"operation":"append analysis note"}
print(json.dumps(record,sort_keys=True))
lab.cleanup(){"operation": "append analysis note", "source_sha256": "48042d70c5edad6ce571fac57a3778e96a6f2daf68f5775afa2168e6cc86d4f1", "working_sha256": "22b664fe36ffa46c320c9dbba3c53a0abd8a9a3869619ea920be344372a8d830"}演習完了