결제대행사 콜백이 봇 필터에 막혀 정산 PENDING이 쌓인 문제 해결
목차
결제대행사 콜백이 봇 필터에 막힌다는 걸 정산 화면에서 발견했을 때, 솔직히 첫 반응은 "이게 왜 여기서 나와?" 였다. 결제 자체는 정상 완료됐는데 내부 상태가 PENDING에서 안 넘어가고 있었고, 그게 며칠째 조용히 쌓이는 중이었다. 서비스 장애로 튀어나온 게 아니라 운영 숫자 보다가 발견한 것 자체가 이미 문제인 상황.
왜 콜백이 봇 필터에 걸렸나
보안 필터를 전체 외부 API 경로에 일괄 적용하는 건 처음엔 편하다. 미들웨어 한 줄로 전 경로 커버가 되니까. 문제는 그 "전 경로"에 파트너 콜백도 포함된다는 걸 나중에 잊는다는 거다. 사용자 브라우저 트래픽과 결제대행사 서버 트래픽은 UA도 다르고 IP 대역도 다르고 헤더 구성도 다르다. 근데 필터는 구분 없이 같은 점수 모델을 돌린다.
이번 케이스에서 콜백은 결제대행사 자체 헬스체크용 UA로 날아왔다. 해당 UA는 봇 점수 임계치를 훌쩍 넘겼고, 필터가 4xx를 던지면서 콜백 바디 자체가 서버까지 닿질 않았다. 원래대로라면 서명 헤더를 검증하고 PENDING 상태를 완료로 갱신했을 흐름이 필터 레이어에서 잘려버린 것.
여기서 생각해봐야 할 구조 문제가 있다. 봇 필터와 인증은 보통 같은 계층(미들웨어/필터 체인)에 걸리는데, 봇 필터가 앞에 있으면 인증 로직이 호출조차 안 된다. 콜백의 신뢰 근거는 "서명 헤더"인데, 그걸 검증할 기회 자체가 차단된 셈이다. 보안을 강화하려다 오히려 올바른 보안 검증을 막은 구도.
더 시간을 잡아먹은 건 차단 로그였다. 분기별 라벨링이 없어서 4xx 로그를 봐도 "왜 막혔는지"를 알 수가 없었다. UA 점수 초과인지, IP 평판 문제인지, 헤더 누락인지. 원인을 좁히는 데만 시간을 한참 더 날렸다. 로그가 있어도 쓸 수 없으면 없는 거랑 같다.
필터 체인 재설계
수정 방향은 세 가지로 정리했다.
콜백 경로를 필터 화이트리스트에 추가한다. 경로가 매칭되면 봇 점수 평가를 건너뛰고 서명 검증으로 바로 넘긴다. 화이트리스트 패턴은 최대한 좁게 잡아야 한다. 경로 패턴이 넓으면 우회 공격 벡터가 생긴다.
화이트리스트 통과 요청은 서명 검증이 전담한다. 콜백의 신뢰 판단 기준은 봇 점수가 아니라 HMAC 서명이다. 서명 불일치면 401/403을 서명 검증 레이어에서 반환하도록 구성했다. 필터가 아니라 검증 로직이 최종 판단을 내리는 게 맞다.
차단 카운터에 reason 필드를 추가한다. 기능 변경이 아니라 운영 가시성 문제다. 이 하나가 생기면 다음 사고 분석 시간을 크게 줄일 수 있다.
흐름을 정리하면:
if (콜백_경로_매칭) {
서명_검증_또는_401()
chain.doFilter()
return
}
봇_점수_평가()
if (봇_점수 > 임계치) {
counter.increment(reason = "ua_score" | "ip_reputation" | "header_missing")
throw 429
}
chain.doFilter()
reason 필드 하나가 생기면 로그 한 줄로 원인 분류가 된다. 다음에 비슷한 일이 생겼을 때 가설 세우는 시간을 건너뛸 수 있다.
배포 후 5일간 결과:
| 항목 | 변경 전 | 변경 후 |
|---|---|---|
| 콜백 차단율 | 약 12% | 0% |
| PENDING 잔여 건 | 일 30건 | 0건 |
| 평균 응답 | 80ms | 75ms |
| 운영 알람 | 일 3건 | 0건 |
응답 시간이 소폭 줄어든 건 불필요하게 봇 점수 평가를 돌리던 요청들이 빠졌기 때문이다. 5일간 누락 케이스 0건.
회고 - 구조적 문제는 아직 남아 있다
이번에 고친 건 증상 대응에 가깝다. 화이트리스트에 경로 하나 추가하고, 로그에 reason 박은 것. 기술 부채를 해소한 게 아니라 다음 사고를 늦췄을 뿐이다.
파트너 연동이 늘어날수록 "필터 예외 목록"이 길어지는 구조 자체가 안티패턴이다. 경로를 추가할 때마다 담당자가 "이 요청이 봇 필터를 피해야 하나?" 를 판단해야 하는데, 그 판단이 빠지면 이번 같은 사고가 반복된다. 중장기적으로는 인증 게이트를 별도 필터 체인으로 분리하는 게 맞다.
- 신뢰 근거가 서명/토큰인 파트너 트래픽은 파트너 인증 체인
- 신뢰 근거가 세션/쿠키인 사용자 트래픽은 일반 보안 체인
이 둘은 처음부터 다른 파이프라인을 타야 한다. 보안 필터를 한 곳에 몰아놓으면 "외부에서 들어오는 콜백"이라는 카테고리를 빠뜨리기 쉽다. 설계 단계에서 트래픽 출처를 기준으로 파이프라인을 분리해뒀으면 화이트리스트 관리 자체가 필요 없었을 수도 있다.
정산 모니터링 얘기를 안 하고 넘어갈 수 없다. 콜백이 막히기 시작한 시점과 정산 이상을 발견한 시점 사이에 간격이 있었다. 콜백 실패는 콜백 레이어에서 즉시 감지했어야 했다. 콜백 실패율 임계치 알람, PENDING 건수 급증 알람 중 하나라도 있었으면 훨씬 빨리 잡혔을 거다. 이 부분이 사실 이번 코드 수정보다 더 임팩트 있는 후속 과제다. 사고는 고쳤지만 다음 사고를 얼마나 빨리 감지할 수 있는지는 아직 미지수.
결제대행사 측에 콜백 IP 대역도 한 번 더 받아뒀다. 화이트리스트를 경로 기반에서 경로+IP 대역 기반으로 전환하면 검증 계층이 하나 더 생긴다. 서명 검증이 뚫렸을 때의 안전망 역할이다. 보안 레이어는 귀찮아도 여러 겹으로 쌓는 게 맞다. 이번 사고가 그걸 다시 한번 확인시켜줬다.
댓글 0
첫 댓글 달아줘.