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

  • 보안 사고 보고서는 어떤 사실, 판단, 불확실성을 분리해 기록해야 하는가?
  • 재발 방지 항목이 실행 가능한 조치인지 어떻게 검증하는가?
  • 사고 종료 선언 전에 로그, 테스트, 운영 절차를 어떻게 확인해야 하는가?

개요

보안 사고 보고서는 사건을 예쁘게 정리하는 문서가 아니라 나중에 같은 사고를 막기 위한 운영 증거다.

좋은 보고서는 타임라인, 영향 범위, 근본 원인, 완화 조치, 남은 위험, 재발 방지 소유자를 분리한다.

모르는 부분은 숨기지 말고 확인 중, 로그 부재, 추정으로 표시해야 한다.

보고서가 비난 문서가 되면 사람들은 다음 사고에서 증거와 실수를 숨기게 된다.

공격 시나리오

  • 사고 중 급하게 배포한 hotfix가 어떤 증거를 지웠는지 아무도 기록하지 않는다.
  • 영향 계정 수가 확정되지 않았는데 외부 공지에는 확정 표현을 사용한다.
  • 근본 원인을 개발자 실수로 적고 누락된 테스트, 로그, 리뷰 절차를 고치지 않는다.
  • 재발 방지 항목이 담당자와 기한 없이 보안 강화로만 남는다.
  • 한 달 뒤 같은 endpoint에서 같은 사고가 반복되지만 이전 보고서와 연결되지 않는다.

취약한 요청/응답 예시

Incident: API security issue
Impact: maybe some users
Cause: missing validation
Action: fixed in production
Next: monitor

이런 보고서는 무엇이 언제 발생했고, 어떤 사용자가 영향을 받았고, 어떤 검증으로 종료했는지 알 수 없다.

INFO hotfix deployed
INFO logs deleted to save disk

증거 보존 없이 hotfix와 로그 삭제가 먼저 일어나면 나중에 영향 범위를 복원하기 어렵다.

주제별 핵심 판단

  • 보고서는 사실, 판단, 추정, 남은 의문을 분리해야 한다.
  • 타임라인은 최초 발생, 최초 탐지, 최초 완화, 영구 수정, 종료 선언을 포함한다.
  • 영향 범위는 계정 수만이 아니라 resource type, action, 데이터 민감도, 기간으로 적는다.
  • 근본 원인은 사람 이름이 아니라 통제 실패로 적는다.
  • 재발 방지는 코드, 테스트, 로그, 경보, 운영 절차 중 무엇이 바뀌는지 보여야 한다.
  • 종료 선언은 조치 완료가 아니라 검증 완료를 기준으로 한다.

개선된 요청/응답 예시

Incident ID: IR-2026-07-01-003
Type: authorization bypass
Detected at: 2026-07-01T02:40:00Z
First known affected request: 2026-06-30T09:10:00Z
Affected resource: order detail
Confirmed affected accounts: 7
Confirmed affected resources: 184
Containment: order detail export disabled at 2026-07-01T03:05:00Z
Permanent fix: owner condition added to service policy and repository query
Verification: non-owner API tests added to CI, access log backfill reviewed
Open uncertainty: CDN access log before 2026-06-30T09:00:00Z unavailable
level=INFO event=incident.action.completed incidentId=IR-2026-07-01-003 action=add_non_owner_authz_tests owner=backend-security due=2026-07-05 status=verified traceId=ir-20260701-003

방어 설계

  • 사고 보고서 template에는 timeline, scope, root cause, actions, evidence, uncertainty, owners가 있어야 한다.
  • 모든 시각은 timezone을 명시하고 가능한 한 UTC 기준으로 기록한다.
  • 증거 링크는 변할 수 있는 dashboard 화면보다 보존된 query, log export, ticket, commit hash를 우선한다.
  • 사용자 공지 문구는 확정 사실과 권장 조치를 분리한다.
  • 재발 방지 항목은 담당자, 기한, 검증 방법, 완료 조건을 가진다.
  • 사고 후 회고는 blame-free로 진행하되 책임 없는 문서가 되어서는 안 된다.
  • 보고서에는 민감 개인정보와 secret을 직접 붙이지 말고 보존 위치와 접근 권한을 기록한다.
  • 종료 후에도 동일 유형 경보를 일정 기간 강화해 조치 효과를 확인한다.

Spring/HTTP 예시

record IncidentAction(
    String incidentId,
    String action,
    String owner,
    LocalDate dueDate,
    String verification,
    IncidentActionStatus status
) {
    boolean isClosable() {
        return status == IncidentActionStatus.VERIFIED
            && verification != null
            && !verification.isBlank();
    }
}
POST /internal/incidents/IR-2026-07-01-003/actions HTTP/1.1
Content-Type: application/json
 
{
  "action": "add_non_owner_authz_tests",
  "owner": "backend-security",
  "dueDate": "2026-07-05",
  "verification": "CI에서 OrderAuthorizationTest.nonOwnerCannotRead 통과"
}

사고 조치 항목은 할 일 목록이 아니라 검증 가능한 운영 객체로 다루어야 한다.

운영 로그와 감사 지점

level=INFO event=incident.timeline.updated incidentId=IR-2026-07-01-003 step=containment_completed at=2026-07-01T03:05:00Z evidence=change-9812 traceId=ir-20260701-003
level=INFO event=incident.closed incidentId=IR-2026-07-01-003 closedBy=security-lead verification=all_actions_verified residualRisk=accepted traceId=ir-20260701-003
  • 사고 상태 변경, 영향 범위 업데이트, 사용자 공지, 조치 완료, 종료 선언을 감사 이벤트로 남긴다.
  • 영향 범위 산정 쿼리와 결과 생성 시각을 함께 보존한다.
  • 보고서 수정 이력은 누가 무엇을 바꾸었는지 남긴다.
  • 재발 방지 항목이 지연되면 일반 업무 지연이 아니라 보안 위험으로 표시한다.
  • 종료 후 같은 탐지 규칙이 다시 울리면 이전 incident id와 연결한다.

실전 판단 기준

  • 빠른 보고서보다 틀린 확정 표현을 피하는 것이 더 중요하다.
  • 모르는 부분을 남긴 보고서가 거짓 확신을 주는 보고서보다 낫다.
  • 근본 원인은 누가 빠뜨렸는가보다 왜 빠져도 통과했는가를 묻는다.
  • 재발 방지에 테스트가 없으면 같은 실수가 다시 들어올 가능성이 높다.
  • 로그 개선만으로는 재발 방지가 아니다. 경보와 대응 절차까지 이어져야 한다.
  • 사용자 공지는 법무, 개인정보, 고객지원과 함께 사실 기준을 맞춘다.
  • 종료 선언은 담당 팀의 감정이 아니라 검증 증거로 한다.

테스트 포인트

  • 보고서 template에 timeline, impact, root cause, actions, owners, uncertainty가 모두 있는지 확인한다.
  • 각 재발 방지 항목에 담당자, 기한, 검증 방법이 있는지 확인한다.
  • 영향 범위 산정 쿼리를 다른 사람이 다시 실행해 같은 결론에 도달할 수 있는지 확인한다.
  • 사고 종료 전에 관련 회귀 테스트와 경보 규칙이 실제로 배포되었는지 확인한다.
  • 보고서에 token, password, 주민번호, 카드번호 같은 민감값이 붙어 있지 않은지 확인한다.
  • 같은 유형의 이전 사고와 연결해 반복 원인이 있는지 확인한다.

위험 신호!

  • 보고서가 fixed 한 단어로 끝나고 영향 범위가 없다.
  • 재발 방지 항목에 담당자와 기한이 없다.
  • 근본 원인이 사람 이름이나 실수로 끝난다.
  • 증거 링크가 만료되는 dashboard URL뿐이다.
  • 종료 선언 전에 테스트, 경보, 운영 절차 변경을 확인하지 않는다.
  • 외부 공지와 내부 보고서의 영향 범위 기준이 다르다.

확인 질문

확인 질문

  • 사고 보고서에서 사실과 추정을 나누어야 하는 이유는 무엇인가?
    • 잘못된 확정 표현은 사용자 공지, 법적 판단, 후속 조치를 모두 왜곡할 수 있기 때문이다.
  • 좋은 재발 방지 항목의 조건은 무엇인가?
    • 소유자, 기한, 검증 방법, 완료 조건이 있고 코드나 운영 절차 중 무엇이 바뀌는지 분명해야 한다.
  • 사고 종료 선언 전 마지막으로 확인할 것은 무엇인가?
    • 영향 범위 산정, 완화 조치, 영구 수정, 회귀 테스트, 경보/로그 개선, 남은 위험 승인이다.

참고 문서