이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.

  • Audit log와 security event는 목적과 필드가 어떻게 다른가?
  • 보안 이벤트는 어떤 구조로 남겨야 검색, 탐지, 사고 대응에 쓸 수 있는가?
  • Java/Spring 애플리케이션에서는 어떤 이벤트를 어디에서 발행해야 하는가?

개요

Audit log는 나중에 “누가, 언제, 무엇을, 어떤 권한으로, 어떤 결과로 수행했는가”를 복원하기 위한 기록이다. 보안 사고, 내부 감사, 고객 문의, 분쟁 대응에서 사실관계를 확인하는 증거가 된다.

Security event는 탐지와 대응을 위한 신호다. 로그인 실패 급증, token 재사용, 권한 거부, signature mismatch, 비정상 결제 시도처럼 즉시 관찰하고 알림으로 이어질 수 있다.

둘은 겹치지만 같지 않다. 감사 로그는 무결성과 보존이 중요하고, 보안 이벤트는 탐지 속도와 상관관계가 중요하다. 같은 행위가 두 목적을 모두 가질 수도 있다.

이 문서의 핵심은 “로그를 남긴다”가 아니라 “나중에 믿고 검색할 수 있는 구조화된 증거를 남긴다”다.

공격 시나리오

  • 공격자는 로그인 실패를 여러 IP와 여러 계정으로 분산해 단일 threshold를 피한다.
  • 권한이 없는 리소스 접근을 반복하면서 403 로그가 자유 문장으로만 남아 집계되지 않게 만든다.
  • 관리자 권한 변경 후 대량 조회를 수행하지만 두 이벤트가 같은 trace나 actor로 연결되지 않는다.
  • 사고 후 로그 조회 권한이 넓어 민감 데이터가 다시 유출된다.
  • 공격자가 로그 삭제 권한을 얻어 감사 증거를 지운다.
  • request id가 없으면 access log, application log, DB log, provider log를 연결할 수 없다.

취약한 요청/응답 예시

INFO failed login
INFO admin changed
WARN bad token abc.def.ghi for user joseph@example.com from 203.0.113.10
HTTP/1.1 403 Forbidden
X-Request-Id: missing

자유 문장 로그는 검색과 집계가 어렵고, token과 이메일 원문이 섞인다. request id가 없어서 같은 요청의 앞뒤 흐름도 연결되지 않는다.

주제별 핵심 판단

  • 이벤트 이름은 안정적인 식별자여야 한다.
  • actor와 subject를 구분한다. actor는 행위자이고 subject는 영향을 받은 대상이다.
  • resource, action, result, reason은 별도 필드로 남긴다.
  • traceId/requestId는 시스템 사이 로그를 연결하는 핵심 열쇠다.
  • timestamp는 UTC 기준으로 남기고 clock skew를 관찰한다.
  • 감사 로그 저장소의 write, read, delete 권한을 분리한다.
  • 로그 스키마 변경은 애플리케이션 API 변경처럼 versioning한다.

개선된 요청/응답 예시

level=WARN event=authorization_denied actor=member:1004 subject=member:1004 resource=order:9001 action=read result=blocked reason=owner_mismatch sourceIp=203.0.113.10 userAgentHash=1d0a traceId=4a12f9e0
level=INFO event=admin_role_changed actor=admin:7 subject=member:1004 resource=role:manager action=grant result=allowed reason=ticket-8821 traceId=4a12f9e0

각 필드가 분리되어 있으면 계정별, IP별, action별, reason별 검색과 알림이 가능하다.

방어 설계

  • 보안 이벤트 catalog를 만든다.
  • 이벤트마다 목적, severity, 필수 필드, 민감 필드, 보존 기간, 알림 조건을 정의한다.
  • 애플리케이션 code path에서 이벤트를 발행한다. 외부 WAF나 proxy만으로는 business context를 알기 어렵다.
  • 로그는 구조화된 JSON 또는 key-value 형식으로 남긴다.
  • request 시작 시 traceId를 만들고 모든 하위 로그에 전달한다.
  • token, password, secret, 전체 개인정보는 log sanitizer에서 차단한다.
  • 감사 로그는 append-only 또는 변경 추적 가능한 저장소에 보관한다.
  • 로그 조회 권한과 export 권한을 업무 역할별로 제한한다.

Spring/HTTP 예시

public record SecurityEvent(
    String event,
    String actor,
    String subject,
    String resource,
    String action,
    String result,
    String reason,
    String traceId
) {}
@Component
public class AuthenticationAuditListener {
 
    @EventListener
    void onSuccess(AuthenticationSuccessEvent event) {
        audit.record(SecurityEventFactory.loginSucceeded(event.getAuthentication()));
    }
 
    @EventListener
    void onFailure(AbstractAuthenticationFailureEvent event) {
        audit.record(SecurityEventFactory.loginFailed(event));
    }
}

Spring Security의 인증 이벤트를 받아 애플리케이션 공통 audit service로 넘긴다. 업무 이벤트, 예를 들어 결제 승인과 권한 변경은 service layer에서 별도로 발행한다.

운영 로그와 감사 지점

level=INFO event=audit_event_written result=allowed eventType=admin_role_changed storage=audit-log-v2 retention=365d traceId=4a12f9e0
level=ERROR event=audit_event_write_failed result=error eventType=payment_approved storage=audit-log-v2 action=fail_closed traceId=4a12f9e0
  • 감사 로그 쓰기 실패 시 업무를 계속할지 중단할지 event별로 정책을 정한다.
  • 보안 이벤트 발행 실패도 관측해야 한다.
  • 로그 수집 지연과 누락률을 metric으로 본다.
  • 로그 저장소 접근과 export 자체도 감사 이벤트로 남긴다.
  • 이벤트 schema validation 실패는 배포 품질 문제로 다룬다.

실전 판단 기준

  • message 하나에 모든 내용을 넣으면 탐지와 통계가 어렵다.
  • actor와 target을 구분하지 않으면 관리자 대리 작업을 복원할 수 없다.
  • 보안 이벤트가 application layer 밖에만 있으면 업무 의미가 빠진다.
  • 로그 삭제 권한이 운영자 전체에게 열려 있으면 감사 로그가 증거가 되기 어렵다.
  • request id 없이 microservice 로그를 연결하려 하면 사고 타임라인이 끊긴다.
  • 로그에 민감값이 많으면 로그 시스템이 가장 큰 개인정보 저장소가 된다.

테스트 포인트

  • 로그인 성공/실패 이벤트가 같은 schema로 남는지 확인한다.
  • 권한 거부 이벤트에 actor, resource, action, reason이 있는지 확인한다.
  • 관리자 권한 변경 이벤트에 actor와 subject가 분리되어 있는지 확인한다.
  • 로그 sanitizer가 token, password, session id를 차단하는지 확인한다.
  • traceId로 access log, application log, DB log를 연결할 수 있는지 확인한다.
  • 감사 로그 조회와 export가 별도 감사 이벤트로 남는지 확인한다.

위험 신호!

  • 보안 이벤트 catalog가 없다.
  • 로그가 자유 문장 중심이고 필드가 없다.
  • token 원문이나 이메일 전체가 경고 로그에 남는다.
  • actor와 subject가 모두 user 하나로 기록된다.
  • request id 또는 trace id가 없다.
  • 감사 로그 저장소를 누구나 수정하거나 삭제할 수 있다.

확인 질문

확인 질문

  • audit log와 security event는 어떻게 다른가?
    • audit log는 사실관계 복원을 위한 증거이고 security event는 탐지와 대응을 위한 신호다.
  • 보안 이벤트의 필드가 분리되어야 하는 이유는 무엇인가?
    • 계정별, 리소스별, reason별 검색과 상관관계 분석이 가능해지기 때문이다.
  • 감사 로그 저장소의 핵심 보안 요구는 무엇인가?
    • 접근 제어, 변조 방지, 보존 정책, 조회/export 감사다.

참고 문서