본문 바로가기
C.W.K.
Stream
Lesson 05 of 05 · published

Audit Log — Tamper-Evident 기록

~15 min · audit-log, tamper-evident, forensics

Level 0Greenhorn
0 XP0/53 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

시도 로그는 로그인을 따라가고, audit 로그는 privileged 액션 을 따라가. 뭔가 잘못되면 기록이 있길 바라게 될 거야. 혼자 쓰더라도 "화요일에 내가 그거 한 것 같은데" 와 "14:32 에 노트북에서 내가 했어" 는 완전히 다르거든.

audit 할 것

액션이유
로그인 성공 / 실패이미 attempts 에 있음. 성공한 줄만 여기 같이 남김
Session revoked (단일)누가 했고, 어떤 session
Revoke All 클릭제일 큰 망치야. 무조건 남겨
PIN 변경주요 보안 이벤트
IP blacklisted / unblocked수동 정책 변경
Config 변경 (max_retry, lockout 윈도우)Drift 감지
복구 토큰 사용마지막 수단이라 무조건 남겨

Tamper-evidence (편집증인 사람들)

"공격자가 audit 로그까지 고쳐서 흔적을 지운다" 를 막고 싶으면 hash 로 줄을 사슬처럼 엮어. 각 줄이 hash(prev_hash + this_row_payload) 를 저장하는 거야. 최신 hash 는 주기적으로 서버 밖에 복사해두고 (Dropbox, S3, 심지어 손으로 적은 포스트잇도). 나중에 검증할 때 사슬이 끊겨 있으면 바로 알 수 있어.

Code

Audit 로그 스키마·sql
CREATE TABLE security_audit (
  id            INTEGER PRIMARY KEY AUTOINCREMENT,
  event         TEXT    NOT NULL,           -- 'revoke_all', 'pin_changed' 등
  actor_ip      TEXT,                       -- 누가 trigger
  details       TEXT,                       -- 추가 위한 JSON blob
  occurred_at   INTEGER NOT NULL
);
CREATE INDEX idx_audit_time  ON security_audit(occurred_at);
CREATE INDEX idx_audit_event ON security_audit(event);
Audit 헬퍼·python
import json, time

def audit_log(event: str, actor_ip: str = None, **details):
    db.execute("""
      INSERT INTO security_audit(event, actor_ip, details, occurred_at)
      VALUES (?, ?, ?, ?)
    """, (event, actor_ip, json.dumps(details), int(time.time())))
    db.commit()

External links

Exercise

security_audit 테이블과 audit_log 헬퍼 추가. 기존 admin 엔드포인트에 wire: revoke-all, unblock, kill-ip, incident-mode. 이제 admin UI 에서 하나씩 눌러보고 줄이 생기는지 확인해. audit 로그는 '내가 한 건가, 남이 한 건가?' 의 답이야. 그 질문이 나오기 전에 미리 투자해두는 거지.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.