이 문서를 통해 아래 질문들에 답할 수 있게 됩니다.
- 로그인, 권한 변경, 결제 이벤트는 왜 따로가 아니라 연결해서 봐야 하는가?
- 계정 탈취에서 금전 피해까지 이어지는 타임라인을 어떤 필드로 복원할 수 있는가?
- 고객센터 수동 처리와 관리자 작업은 어떻게 감사해야 하는가?
개요
로그인, 권한 변경, 결제는 각각 독립 이벤트처럼 보이지만 사고에서는 하나의 흐름으로 이어진다. 공격자는 계정을 탈취하고, MFA나 recovery 정보를 바꾸고, 권한을 얻거나 결제를 시도한다.
따라서 이벤트를 개별 로그로만 남기면 부족하다. 같은 actor, subject, device, IP range, session, traceId, risk signal로 묶을 수 있어야 한다.
특히 권한 변경과 결제는 피해가 직접 발생하는 고위험 이벤트다. 성공 이벤트만 중요한 것이 아니라 실패, 거부, 취소, 환불, 수동 승인도 함께 봐야 한다.
이 문서의 핵심은 “중요 이벤트를 남긴다”가 아니라 “중요 이벤트 사이의 순서와 상관관계를 복원한다”는 것이다.
공격 시나리오
- 공격자는 credential stuffing으로 로그인에 성공한다.
- 성공 직후 비밀번호, MFA, 이메일, 전화번호를 변경해 계정 복구 경로를 장악한다.
- 권한 변경 요청이나 팀 초대 기능을 이용해 높은 권한을 얻는다.
- 결제 수단 추가, 출금 계좌 변경, 고가 결제, 환불 요청을 수행한다.
- 고객센터에 접근해 수동 승인이나 본인확인 우회를 유도한다.
- 각각은 정상 기능이지만 짧은 시간에 연속되면 강한 위험 신호다.
취약한 요청/응답 예시
INFO login ok user=1004
INFO profile updated user=1004
INFO payment ok amount=1200000
INFO admin changed user=1004HTTP/1.1 200 OK
X-Request-Id: missing로그가 너무 짧아 device, actor, target, 변경 전후 값, 결제 수단, risk reason을 알 수 없다. 사고 조사자는 “무슨 순서로 무엇이 바뀌었는지”를 재구성하기 어렵다.
주제별 핵심 판단
- 로그인 이벤트에는 성공/실패, MFA, device, IP, user agent, risk score를 남긴다.
- 계정 보안 설정 변경은 old/new 상태와 actor를 남긴다.
- 권한 변경은 grant/revoke, old role, new role, approver, ticket id를 남긴다.
- 결제 이벤트는 amount, currency, payment method reference, merchant/order id, result를 남긴다.
- 민감값 원문은 남기지 않는다. 카드번호, 계좌번호, token은 reference나 masked value만 쓴다.
- 고위험 이벤트는 같은 session과 최근 이벤트를 함께 평가한다.
- 수동 처리도 자동 처리와 동일한 schema로 감사해야 한다.
개선된 요청/응답 예시
level=INFO event=login_succeeded actor=member:1004 subject=member:1004 deviceId=d-77 ipBucket=203.0.113.0/24 mfa=passed risk=medium traceId=8842d0a1level=WARN event=payment_high_risk result=blocked actor=member:1004 subject=order:8001 amount=1200000 currency=KRW reason=login_new_device_and_mfa_changed traceId=8842d0a1두 이벤트가 같은 actor와 짧은 시간 창으로 연결되면 단일 결제 로그보다 훨씬 강한 탐지 근거가 된다.
방어 설계
- high-value event 목록을 만든다.
- 이벤트마다 actor, subject, resource, action, old value, new value, result, reason을 정한다.
- 로그인 성공 후 일정 시간 안의 보안 설정 변경과 결제 시도에 추가 검증을 붙인다.
- 권한 변경은 관리자 actor와 대상 subject를 분리하고 승인 근거를 남긴다.
- 결제와 환불은 idempotency key, order id, payment reference로 추적한다.
- 고객센터 수동 처리에는 상담원 actor, ticket id, 승인자, 변경 전후 값을 남긴다.
- risk rule은 차단, 추가 인증, 알림, shadow mode 중 어떤 action인지 명시한다.
- 사용자에게 고위험 변경 알림을 보내고 알림 실패도 기록한다.
Spring/HTTP 예시
public void changeRole(Admin admin, Member target, Role newRole, String ticketId) {
Role oldRole = target.role();
authorizationService.requireRoleAdmin(admin);
target.changeRole(newRole);
memberRepository.save(target);
audit.record(SecurityEvent.roleChanged(
"admin:" + admin.id(),
"member:" + target.id(),
oldRole.name(),
newRole.name(),
ticketId
));
}public PaymentDecision decide(PaymentRequest request, Member member) {
RiskContext context = riskService.recentContext(member.id(), Duration.ofMinutes(30));
if (context.hasNewDeviceLogin() && context.hasMfaChanged()) {
return PaymentDecision.requireStepUp("new_device_and_mfa_changed");
}
return PaymentDecision.allow();
}업무 service에서 이벤트를 발행해야 old/new value와 business reason을 정확히 남길 수 있다.
운영 로그와 감사 지점
level=INFO event=mfa_factor_changed actor=member:1004 subject=member:1004 action=replace oldFactor=totp newFactor=passkey result=allowed traceId=8842d0a1level=INFO event=manual_refund_approved actor=support:31 subject=payment:pay_922 amount=120000 currency=KRW ticketId=CS-18821 approver=manager:9 traceId=8842d0a1- 결제 수단은 원문 대신 provider reference와 masked value만 남긴다.
- 권한 변경은 old/new role을 반드시 남긴다.
- MFA 변경은 factor type과 actor를 남기되 secret은 남기지 않는다.
- 고객센터 수동 처리는 ticket id 없이 허용하지 않는다.
- 고위험 이벤트 후 사용자 알림 발송 성공/실패를 남긴다.
실전 판단 기준
- 로그인 성공 로그만 있고 MFA 결과가 없으면 계정 탈취 분석이 약하다.
- 권한 변경 로그에 변경 전 값이 없으면 rollback과 피해 범위 산정이 어렵다.
- 결제 이벤트에 금액과 결제 reference가 없으면 금전 피해를 집계할 수 없다.
- 고객센터 수동 작업이 감사 로그 밖에 있으면 공격자는 자동 방어를 우회할 수 있다.
- high-value event가 같은 trace나 correlation id로 묶이지 않으면 타임라인이 끊긴다.
- 사용자 알림이 없으면 탐지와 대응이 서버 안에서만 끝난다.
테스트 포인트
- 새 device 로그인 직후 결제 시도가 risk rule에 걸리는지 확인한다.
- MFA 변경 직후 고액 결제가 step-up 인증을 요구하는지 확인한다.
- 권한 grant/revoke 이벤트에 old/new role과 actor가 남는지 확인한다.
- 결제 승인/거부/환불이 같은 order id와 payment reference로 연결되는지 확인한다.
- 고객센터 수동 변경에 ticket id와 approver가 없으면 거부되는지 확인한다.
- 고위험 이벤트 사용자 알림 실패가 운영 알림으로 이어지는지 확인한다.
위험 신호!
- 로그인, 권한 변경, 결제 로그가 서로 다른 id 체계로 연결되지 않는다.
- 권한 변경에 actor와 subject가 분리되어 있지 않다.
- 결제 로그에 금액, currency, order id가 없다.
- 고객센터 수동 작업이 별도 감사 없이 DB를 직접 수정한다.
- 고위험 이벤트 후 사용자 알림이 없다.
- 이벤트가 성공 케이스만 남고 실패/거부/취소는 남지 않는다.
확인 질문
확인 질문
- 로그인과 결제 이벤트를 연결해서 봐야 하는 이유는 무엇인가?
- 계정 탈취 후 금전 피해로 이어지는 흐름은 단일 이벤트보다 시간 순서와 상관관계에서 드러나기 때문이다.
- 권한 변경 이벤트의 필수 필드는 무엇인가?
- actor, subject, old role, new role, action, result, reason 또는 ticket id, traceId다.
- 고객센터 수동 처리는 왜 별도 감사가 필요한가?
- 자동 보안 정책을 우회할 수 있는 강한 권한 경로이기 때문이다.