본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 20. 23:59

AI 코딩 에이전트 세션을 위한 암호학적 포렌식 (Cryptographic Forensics)

요약

AI 코딩 에이전트(Claude Code, Codex CLI 등)가 인프라에 치명적인 명령을 실행했을 때, 기록된 로그의 무결성을 보장하기 위한 암호학적 포렌식 방안을 다룹니다. DEPOSE 프로젝트는 해시 체인, Ed25519 서명, RFC 3161 타임스탬프를 결합하여 변조가 불가능하고 검증 가능한 이벤트 기록 시스템을 제안합니다.

핵심 포인트

  • 기존 에이전트 관찰성 도구는 디버깅용일 뿐, 사고 조사 시 로그의 신뢰성을 보장하지 못함
  • 신뢰할 수 없는 환경에서도 로그를 검증하기 위해 변조 탐지, 인증, 소급 적용 방지라는 세 가지 암호학적 속성이 필요함
  • LLM이 생성한 서사(narrative)는 검증 대상에서 제외하고, 결정론적인 템플릿을 통해 서명된 이벤트와 연결해야 함
  • DEPOSE는 정규화된 이벤트 스키마와 해시 체인을 통해 감사인이 검증 가능한 데이터 번들을 생성함

Claude Code 또는 Codex CLI 세션은 디스크에 JSONL 파일을 작성합니다. 만약 에이전트가 학습 데이터 디렉토리에 대해 rm -rf를 실행하거나, 운영 환경에서 terraform destroy -auto-approve를 실행한다면, 해당 파일은 사고 검토(incident review)가 시작되는 지점이 됩니다. 하지만 JSONL 파일 그 자체는 증거가 아닙니다. 셸(shell) 접근 권한이 있는 사람이라면 누구나 이를 다시 작성할 수 있기 때문입니다. 파일이 생성된 머신을 신뢰하지 않는 제3자에게 이 파일은 아무것도 증명하지 못합니다. 에이전트가 실제 인프라에 대한 자격 증명(credentials)을 갖게 되는 순간, 이러한 격차는 매우 중요해집니다. 대부분의 에이전트 관찰성(observability) 도구는 디버깅과 품질 관리를 위해 구축되었을 뿐, 피해가 발생한 직후의 순간을 위해 만들어지지 않았습니다. 이 포스트는 기록(transcript)을 감사인(auditor)이나 규제 기관이 검증할 수 있는 무언가로 바꿔주는 세 가지 암호학적 속성과, DEPOSE 프로젝트가 이를 어떻게 하나로 연결하는지에 대해 다룹니다.

세 가지 속성
번들을 생성한 머신을 신뢰할 수 없다고 가정합니다. 다음 세 가지가 동시에 유지되어야 합니다:

  1. 변조 탐지 가능 (Tamper-evident): 어떤 바이트 변경이라도 감지할 수 있어야 합니다. 이벤트에 대한 해시 체인(Hash chain)을 사용하여, 단 하나의 바이트만 바뀌어도 재생(replay)이 실패하도록 합니다.
  2. 인증됨 (Authenticated): 기록은 생성자가 제어하고 지문(fingerprint)을 공개하는 키와 결합되어야 합니다. 매니페스트(manifest)에 대한 Ed25519 서명을 사용합니다.
  3. 소급 적용 방지 (Anti-backdated): 생성자 이외의 당사자가 기록을 시간상에 고정(anchor)해야 합니다. 공공 TSA(Time Stamping Authority)로부터 RFC 3161 토큰을 받습니다.

이러한 기본 요소(primitives)들은 오래되었고 잘 알려져 있습니다. 어려운 점은 이를 정규화된 이벤트 스키마(normalized event schema)를 통해 연결하고, 생성자의 런타임(runtime)에 의존하지 않는 검증기(verifier)를 배포하는 것입니다.

서명 경로에 LLM 포함 금지
모든 이벤트는 실행 시점에 캡처되거나 세션 JSONL로부터 정규화된 후, 해시 체인에 커밋됩니다. 사람이 읽을 수 있는 서사(narrative)는 서명된 이벤트들을 바탕으로 결정론적인 Handlebars 템플릿을 사용하여 별도로 생성됩니다. 이는 루트 해시(root hash)에서 제외됩니다. 만약 생성된 산문(prose)이 서명된 기록의 일부가 된다면, 검증은 모델의 동작이 안정적이고 재현 가능하게 유지되는지에 의존하게 될 것입니다. DEPOSE는 그러한 의존성을 피합니다. 서명된 기록은 이벤트 데이터와 해시입니다. 산문은 서명된 이벤트로 다시 연결되는 [#evt-<ulid>] 인용을 포함한 템플릿 기반의 주석입니다.

검증에 영향을 주지 않고도 서사(narrative)를 다시 작성할 수 있습니다. 하지만 이벤트를 변경하면 검증은 실패합니다.

번들(bundle)에는 무엇이 들어있을까요? DEPOSE 번들은 불투명한 아카이브(opaque archive)가 아니라 디렉토리입니다:

incident-01JABC...
├── manifest.json: bundleId, rootHash, eventsJsonlSha256, sigs, timestamps
├── events.jsonl: manifest에 의해 바이트 단위로 고정(byte-pinned)된, 정규 JSON(canonical JSON) 형식의 모든 이벤트
├── rules/destructive.yaml: 재구성(reconstruction) 시 사용되는 규칙 세트(ruleset)
├── narrative.md / .html: 이벤트별 인용이 포함된 템플릿 기반의 산문
├── verify.txt: 사람이 읽을 수 있는 검증 요약
├── artifacts/: 캡처된 파일 차이(diffs), 페이로드(payloads)
├── attestations/: Ed25519 서명, RFC 3161 타임스탬프 토큰
└── raw/: 소스 JSONL, 셸 히스토리(shell history), 캡처 기록

events.jsonl, manifest.json, 또는 rules/destructive.yaml 중 단 1바이트만 변경해도 검증은 실패합니다. 정규 JSON(Canonical JSON)은 RFC 8785 (JCS)를 따르며, 이를 통해 Go 기반의 검증기(verifier)가 TypeScript로 생성된 번들을 양측이 서로의 직렬화 도구(serializer)를 신뢰하지 않고도 확인할 수 있습니다.

두 개의 바이너리(binaries): 생성기(producer)는 TypeScript입니다. 검증기(verifier)는 별도로 빌드된 정적 Go 바이너리인 depose-verify입니다. 이러한 분리는 의도적인 것입니다. 당신은 번들을 확인해야 하는 사람(감사관, 상대측 변호인, 규제 기관, 고객의 보안 팀 등)에게 이 바이너리를 전달하기만 하면 되며, 그들은 자신의 머신에서 이를 실행합니다. 생성기 스택(producer stack)은 필요하지 않습니다. 검증이 통과되면 다음과 같이 출력됩니다:

parse OK
signature OK
chain-replay OK
artifacts OK
timestamp OK
PASS bundleId=... rootHash=...

여기서 사용된 암호학(cryptography)은 대부분 기성 제품(off-the-shelf)입니다. 실제 엔지니어링 작업은 정규화(normalization)에 있습니다. 즉, Go와 Node가 동일하게 직렬화하도록 만들고, 캡처 소스 전반에 걸쳐 타이밍과 순서를 정확하게 맞추며, 무엇을 하나의 이벤트로 볼 것인지 아니면 두 개의 이벤트로 볼 것인지 결정하는 작업입니다. 정규 JSON(Canonical JSON)은 지루한 부분입니다. 부동 소수점(float) 포맷팅, 키 순서, 유니코드 이스케이프(unicode escapes) 등에서 Go와 Node는 바이트 단위로 일치해야 합니다. 그렇지 않으면 검증기는 생성기가 문제없다고 생각하는 번들을 거부합니다. tests/conformance/에 있는 교차 언어 준수 벡터(cross-language conformance vectors)가 바로 이 용도로 존재합니다.

검증자(Verifiers)는 명령줄(command line)에서 프로듀서(producer)의 예상 키 지문(key fingerprint)을 고정하고 폐기 목록(revocation list)을 참조할 수 있습니다. 여기서 RFC 3161 타임스탬프(timestamp)는 이중 역할을 수행합니다. 키가 폐기되기 전에 타임스탬프가 찍힌 번들(bundle)은 시간 기반의 고정 상태(time-anchored)를 유지하므로, 나중에 키가 침해되더라도 "이것이 언제 서명되었는가"라는 질문에 답할 수 있습니다.

캡처 모드(Capture modes): 두 가지 모드가 있으며, 커버리지(coverage)가 다릅니다.

재구성(Reconstruction) 모드는 사후에 Claude Code 세션 JSONL을 읽고, 가능한 경우 셸 히스토리(shell history; bash, zsh, fish) 및 git reflog와 비교하여 번들을 구축합니다. 하한선(Lower-bound) 모드입니다. 패키징 후에 무결성(integrity)을 검증할 수 있지만, 캡처 전의 원래 세션 파일이 완전했음을 증명할 수는 없습니다.

능동적 캡처(Active capture)는 Claude Code PreToolUse 훅(hook)과 파괴적인 작업을 수행하는 경향이 있는 바이너리들(terraform, aws, gh, kubectl, psql, gcloud, railway, rm)을 위한 POSIX 셸 심(shim)을 설치합니다. 기록은 실행 시점에 ~/.depose/captures/ 아래에 저장됩니다. 이후 depose 패키지가 이를 세션 JSONL과 병합하여, 커버된 모든 이벤트가 기록된 검증된 실행 전 의도(pre-execution intent)를 갖게 합니다. DEPOSE는 캡처된 이벤트의 무결성을 증명할 수 있습니다. 하지만 계측되지 않은(uninstrumented) 시스템이 모든 것을 캡처했음을 증명할 수는 없습니다. 심(shim) 목록에 없는 바이너리로 셸을 호출하거나 API에 직접 접속하는 에이전트는 여전히 JSONL에 나타나지만, 능동적 캡처 기록은 갖지 못합니다. 커버리지 매트릭스(coverage matrix)는 리포지토리(repo)에 있습니다. macOS 및 Linux만 지원합니다. Windows는 지원되지 않습니다(키 저장소의 POSIX 0600 권한, 심을 위한 POSIX 셸 스크립트 필요). WSL2는 작동합니다.

릴리스 파이프라인(Release pipeline): 릴리스에는 SBOM, 출처 증명(provenance attestations), 서명된 체크섬(signed checksums)이 포함됩니다. 세부 사항은 다음과 같습니다: 양쪽 모두 CycloneDX를 사용하며, SLSA L3 출처(provenance)를 따르고, cosign keyless를 통해 서명된 SHA256SUMS를 제공합니다. CI는 매 푸시(push)마다 체크인된 두 개의 예시 번들(훈련 데이터에 대한 rm -rf, 인프라에 대한 terraform destroy)을 재빌드하고, 세 가지 시맨틱 변조 거부(semantic tamper rejections)를 실행하여 검증자가 실패 시 차단(fails closed)되는지 확인합니다.

현재 대부분의 코딩 에이전트 세션 로그는 일회용 디버그 출력(disposable debug output)처럼 취급되고 있습니다.

에이전트가 인프라 (infrastructure)를 수정할 수 있게 되는 순간, 그러한 가정은 설득력을 잃습니다. https://github.com/Aftermath-Technologies-Ltd/depose

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0