감사로그는 진술이지 증거가 아니다

대부분의 감사증적은 운영자가 고칠 수 있는 테이블의 행이다. 그 차이는 문제가 터지는 날까지 보이지 않고, 그날엔 그 로그밖에 남아 있지 않다.

감사로그가 있다고 말하는 시스템은 많다. 증거를 가진 시스템은 거의 없다.

이 구분은 말장난이 아니다. 감사로그는 시스템이 무슨 일이 있었다고 말하는가에 답한다. 증거는 당신을 믿지 않는 사람에게 무슨 일이 있었는지 증명할 수 있는가에 답한다. 대부분의 구현은 조용히 앞의 것을 내놓고, 회의실에 있는 모두는 뒤의 것을 받았다고 생각한다.

문제의 생김새

전형적인 감사증적은 테이블이다. 시각, 행위자, 행위, 그리고 문맥이 담긴 JSON 한 덩어리가 들어 있다. 그 행을 쓰는 것은 그 행위를 수행한 바로 그 애플리케이션이고, 쓰는 곳은 같은 데이터베이스이며, 쓰는 자격증명도 같다.

즉 그 데이터베이스에 닿을 수 있는 사람은 누구나 거기에 쓸 수 있다. 그 집합은 대부분의 팀이 인정하는 것보다 넓다. 애플리케이션 자신, 프로덕션 접근 권한을 가진 운영 엔지니어, 백업 복원 절차, 누군가 새벽 두 시에 돌린 마이그레이션 스크립트, 그리고 그중 무엇이든 장악한 침입자.

두 번째 실패는 더 조용하다. 삭제는 흔적을 남기지 않는다. 행 하나가 사라져도 테이블은 여전히 완벽하게 일관된다 — 그저 그 사건이 일어난 적 없는 세계를 서술할 뿐이다. 바깥에서 아무도 쪽수를 세지 않는다면, 덧붙이기만 가능한 이야기를 조작하는 일은 쉽다.

“봉인”이 뜻해야 하는 것

기록이 증거로 기능하려면 세 가지가 동시에 성립해야 한다.

  1. 각 기록이 자기 내용을 확약한다. 기록의 정규화된 직렬화에 대한 해시를 쓰는 시점에 계산한다. 나중에 필드를 바꾸면 해시가 맞지 않는다.
  2. 각 기록이 앞선 기록들을 확약한다. 해시를 사슬로 엮으면 뒤따르는 모든 고리를 깨뜨리지 않고는 기록을 지울 수 없다. 삭제가 더 이상 조용하지 않게 된다.
  3. 그 확약에 기록자가 갖고 있지 않은 키로 서명한다. 건너뛰는 게 바로 이 부분이다. 애플리케이션이 다시 계산할 수 있는 해시 사슬은 애플리케이션이 다시 쓸 수 있는 해시 사슬이다. 운영자가 아닌 누군가가 특정 시점의 사슬 상태를 증언해야 한다.

세 번째가 빠지면 사고로부터는 지켜주지만 적으로부터는 지켜주지 못하는 무결성 시스템을 지은 것이다 — 여섯 달 뒤 압박을 받는 상태로, 변호사를 앞에 두고 앉아 있을 당신 자신을 포함해서.

왜 이게 생략되는가

앞의 둘은 싸고 셋째는 분산 시스템 문제이기 때문이다.

기록 하나를 해싱하는 데는 마이크로초가 든다. 사슬로 엮는 데는 조회 한 번이 든다. 그러나 쓰는 시스템이 접근할 수 없는 키로 서명한다는 건 별도의 신뢰 경계를 뜻한다. 다른 키 보관자, 다른 장애 양상, 지연 예산, 그리고 서명자에게 닿지 않을 때 무슨 일이 일어나는지에 대한 결정. 이건 진짜 엔지니어링이라 티켓이 되고, 티켓은 다음 분기가 된다.

그러는 동안 감사 화면은 배포된다. 맞아 보인다. 실제로 맞다 — 그 정확성이 다투어지는 순간 전까지는.

시험

두 범주를 가르는 질문이 하나 있고, 묻는 데 십 초면 된다.

프로덕션 데이터베이스 접근 권한을 가진 누군가가 지난 3월 기록의 필드 하나를 바꾼다면, 어디에서든 무엇이든 알아챌까?

정직한 답이 “아니오”라면 당신이 가진 건 로그다. 쓸모 있는 로그다 — 디버깅에도, 지원에도, 무엇이 잘못됐는지 재구성하는 데도. 다만 누군가 무슨 일이 있었는지를 다투는 날에 필요한 그것은 아니다.

그날은 드물게 온다. 오면, 대개 그 로그만 남아 있고, 그 로그가 쓸모 있을지는 아무도 중요하다고 표시하지 않았던 설계 결정으로 오래전에 이미 정해져 있다.


Axowl은 신원·승인·감사 이벤트를 쓰는 순간에 봉인합니다 — 해시로 엮고, 쓰는 서비스가 갖고 있지 않은 키로 앵커링합니다. 어떻게 동작하는지 보기.