개발 slecs

결제 구간 앱 서명 위변조 트래픽을 단계별로 차단한 방법

목차

배경

모바일 앱에서 서버로 들어오는 요청이 진짜 우리 앱에서 온 게 맞는지 확인할 방법이 필요했음. 그동안 디바이스 식별자만 보고 신뢰했는데, 리패키징된 패키지로 위변조 트래픽이 들어오는 정황이 잡힘. 결제대행사 연동 구간이라 더 미룰 수 없었음.

접근 — X-Sig-Hash 헤더

앱 서명 인증서의 SHA-256 해시를 매 요청마다 헤더에 실어 보내고, 서버에서 화이트리스트와 비교하는 방식으로 감.

항목
헤더명 X-Sig-Hash
알고리즘 SHA-256
비교 대상 서명 인증서 raw bytes
화이트리스트 DB 테이블 + 메모리 캐시

검증 로직은 한 컴포넌트로 모음.

fun verify(header: String?): Result {
    if (header.isNullOrBlank()) return Result.MISSING
    return if (allowed.contains(header.lowercase()))
        Result.OK
    else Result.MISMATCH
}

가장 신경 쓴 부분 — grace period

문제는 이미 깔린 구버전 앱들이었음. 헤더 안 붙이고 들어오는 트래픽을 한 방에 끊으면 결제 흐름이 통째로 막히는 사고가 남. 그래서 enforcing 한 번에 안 가고 단계를 나눔.

  • 1단계 (관찰): 헤더 없거나 mismatch 떠도 통과시킴. 카운트만 적재.
  • 2단계 (경고): 응답에 deprecation 플래그 실어줌. 강제 업데이트 유도.
  • 3단계 (강제): mismatch는 즉시 401, missing은 grace period 종료 후 거부.

지금은 디바이스 등록과 결제 트리거 두 엔드포인트에만 훅을 걸고 1단계로 운영 중. 일주일 로그 돌려보면서 화이트리스트 누락 잡을 예정.

의외로 까다로웠던 것

  • 빌드 변형별 해시 차이: 디버그/스테이징/릴리스 키스토어가 다 다름. 사내 QA 트래픽이 mismatch로 잡혀서 한참 헤맴. 환경별 해시를 다 넣고 환경 라벨도 같이 박아둠.
  • 대소문자 normalize: 클라이언트가 대문자로 보내는 케이스가 있어서 서버에서 lowercase 처리.
  • 누락 vs 위조 구분: 둘 다 차단 대상이지만 메트릭은 분리. 누락은 구버전 지표, mismatch는 보안 지표로 봄.

회고

처음엔 "그냥 막으면 되지" 싶었는데, 설치 베이스가 깔린 앱에서 검증 정책 추가하는 건 결제 장애 한 번이면 끝나는 일이라 grace period 없이는 못 갔음. 단계 차단 + 메트릭 분리로 가니까 그나마 마음 편함. 다음 단계는 mismatch 비율 보고 2단계 승격 시점 정하기.

다음

댓글 0

첫 댓글 달아줘.