티스토리 뷰

Backend

Gmarket Mobile Web Vip 악성 봇 대침투 사건

지마켓 김윤제 2024. 5. 11. 11:28

Gmarket Mobile Web Vip 악성 봇 대침투 사건

안녕하세요 저는 VI Engineering 팀 김윤제입니다.

Gmarket Mobile Web Vip(View Item Page = 상품 상세)를 담당하고 있는 Backend Engineer 입니다.

 

올해 1월 6일부터 Gmarket Mobile Web Vip에 악성 봇 트래픽 유입에 따른 서비스 다운이 주기적으로 발생했었는데요.

한 달 정도 평일, 주말 밤 낮 없이 원인을 찾기 위해 모니터링을 하며 끝나지 않을 것만 같던 고통(왜 나에게만 이런 일이)을 받았었습니다.

 

우선 악성 봇으로 판단이 되자마자 서버의 Scale Up을 하고 Scale Out을 하였으나

이것은 임시방편으로 추후에 봇이 들어오는 케이스가 달라지면 또다시 발생할 문제였습니다.

이에 근본적인 해결책을 고민하다 Gmarket Mobile Web Vip 악성 봇 탐지 기능을 개발하였습니다.

 

자세한 이야기는 아래에서 상세히 다루도록 하겠습니다.


장애 발생

설명하기에 앞서 우선 Vip 환경과 도메인에 대해 설명드리도록 하겠습니다.

Gmarket Mobile Web Vip Api는 .NET Framework 기반 윈도우 환경에 Internet Information Service(IIS)를

운영하고 있는 Legacy System이며 상품 상세 설명 페이지를 보여주기 위해

다양한 내부 API들을 호출 및 조합하여 보여주는 도메인입니다.

 

2024년 1월 초 데이터 센터 서버군 A의 (IIS)가 Down -> 데이터 센터 서버군 B의 (IIS)가 Down이 되었습니다.
(데이터 센터 서버군 A가 Down 되자 트래픽이 데이터 센터 서버군 B로 이동)

이후로도 날짜는 정확하지 않으나, 4시간마다 트래픽이 상승하는 패턴이 나타났고, 간헐

적으로 주로 데이터 서버군 B에 Down이 발생했습니다.

 

에러 발생 후에 로그를 확인하니 DB Connection Pool이 꽉 찼다는 내용뿐이었는데요.

모니터링 시스템을 통해 본 System 자원 그래프는 아래와 같았습니다.

  1. Cpu

 

2. Tcp CurrentSet



3. Http Request

 

4. 응답 시간 그래프

 

 

 

위와 같이 갑자기 그래프가 위로 치는 현상을 일컬어 '기둥현상'이라고 명칭을 지었습니다.

(어떤 분께서는 위 응답 시간 그래프를 보고 '불기둥'이라고 칭하기도 하였습니다.)

정확한 원인을 파악하기 힘들어 아래와 같이 추정을 하였습니다.

  1. 최근에 개발한 형상에 이슈가 있는지 (Db Connection 관련)
    • 실제로 작년 10월 즈음 Db Connection 개선 작업이 있었으나, 평시보다 트래픽이 많은 11월 빅스마일데이 때 문제없이 버텼으며 이후 형상 변화 없었습니다.
    • 또한 형상 변화가 없었음에도 불구하고 갑자기 발생한 장애는 4시간이라는 패턴을 갖기에 코드적으로 문제가 있다기보다는 (기도 메타) 인프라적인 문제일 것이라 생각했습니다.
  1. 타 API의 지연 또는 네트워크 지연
    • 레거시 시스템이다 보니, API 호출 시 타임아웃이 설정돼있지 않고 로그도 남지 않는 로직들이 많았었습니다.
    • 누락된 로직들에 대해 전부 타임 아웃 & 로그 추가하였고 Tcp Dump까지 하였으나 이것이 원인이 아니었습니다.
  1. 서버 & DB Job 이슈
    • 4시간이라는 패턴을 갖기에 서버 내부 또는 DB Service에 4시간마다 Job이 있는 것이 아닌지 의심했었습니다.
    • 이것도 아니였습니다.
  1. 인프라 네트워크 지연
    • 유력 후보 중 하나였습니다. (기도메타)
    • 하지만 아니었습니다..
  1. GSLB (글로벌 로드 밸런싱)
    • 높은 확률적으로 데이터 센터 서버군 B가 Down이 되었었고 데이터 센터 서버군이 동일하게 트래픽을 받는 것 같지 않다는 생각이 들었었습니다.
    • 결론을 보면 연관되어 있습니다.
  1. 서버군 내 로드 밸런싱
    • 혹시나 하는 마음에 서버 군 내에 로드 밸런싱이 정상적으로 되지 않아 일부 서버가 먼저 Down되면서 도미노 현상이 발생한 것은 아닐까 싶었습니다.
    • 이것도 아니었습니다.
  1. 백신 이슈
    • 안타깝게도 최근에 백신 설치 및 이전 백시 설치 이력이 있어서 혹시 백신에 문제가 있는 것이 아닌지 의심했었습니다
    • 이것도 아니었습니다.
  1. 병렬 처리 Thread 문제
    • 정말 이것만큼은 아니었으면 좋겠다고 God에게 기도를 하고, 기도 메타로는 해결할 수 없어 모든 코드를 분석하였으나 형상은 작년 빅스마일데이와 크게 변화가 없었기에 대혼돈에 빠졌습니다.
    • 이것도 아니었습니다 (살았다..)
  1. 트래픽에 따른 CPU 부하 -> Db Connection 수행 불가
    • 트래픽은 오히려 빅스마일데이가 더 높았었고, 그때도 잘 버텼기에 이것은 아니라고 생각했습니다.
    • 결론을 보면 연관되어 있습니다.

 


원인

정확한 원인을 찾지 못해 절망 속에서 헤엄치고 있던 찰나 팀원분들께서 같이 도와주신 결과 Activity log를 통해서 장애를 발생시키는 4시간 패턴의 그래프 형상을 찾았습니다.

 

처음 위 패턴을 찾았을 때만 해도 ipAddress 대역대의 패턴인 것으로 확정을 지어갔었는데 좀 더 확인해 보니 ipAddress는 다양하게 들어오고 있어서 숨겨진 패턴을 찾기 힘들었습니다.

 

그렇게 장애가 발생한 시점에 ipAddress들을 전부 검색해 보다가 특징을 발견했는데, 바로 userAgent가 동일한 케이스가 보이기 시작한 것이었습니다.

 

Activity log를 통해 확인한 특정 UserAgent의 유입량 (4시간 패턴)

 


해결책

userAgent 기반의 악성 봇으로 판단되기 전에는 임시방편으로 Service Down을 최대한 막는 것이 맞겠다는 판단을 하였습니다.

급한 대로 서버의 Scale up과 Scale Out을 한 상태이지만 문제가 있었습니다.

  1. 만약 봇 트래픽이 증가한다면 또다시 Service Down
  2. 비용이 만만치 않게 든다는 것입니다.

userAgent 기반의 악성 봇으로 판단이 된 후에는 집계기능과 동시성 처리가 뛰어난 Redis를 활용해서 탐지기능을 만들면 되겠다 생각했는데요.

간단히 설명드리자면 임의의 userAgent로 N번의 Request가 올 시 특정 임계치(M)를 넘어설 경우 5분간 악성 봇으로 처리한다는 내용입니다.

아래 Activity log에 기록된 UserAgent 기반 악성 봇의 초당 유입량 그래프에서 N의 수치를 설정하였습니다.

(N과 M은 실제 운영을 하며 조절할 예정입니다.)

 

 

정상 유입 Flow

 

userAgent 기반 악성 봇 유입 Flow

ex) userAgent = "Mozilla/5.0 King Gmarket"

 


진짜 봇이면 어떡하려고?

Vip(상품 상세 페이지)는 봇이 들어와야 합니다. (그래야 검색에 노출이 됩니다.)

하지만 진짜 봇을 파악 못하고 차단해 버리는 거 아닌가라는 생각을 할 수 있는데요.

구글 봇의 경우 공식 홈페이지에 userAgent가 기록이 되어있습니다.

이와 같이 공식으로 등록되어 있는 userAgent는 예외 처리로 등록해 두었습니다.

https://developers.google.com/search/docs/crawling-indexing/overview-google-crawlers?hl=ko#googlebot-smartphone


끝으로

끝인 줄 알았습니다.

userAgent기반으로 막으면 될 것 같다는 막연한 생각에 팀원분과 이야기를 나누다가

악성 봇이라고 차단된 userAgent와 동일한 userAgent를 사용하는 진짜 고객이 있을 것 같다. 라는 얘기를 듣자 충격에 빠졌습니다.

 

또한 근본적으로 악성 봇이 4시간 패턴을 가지고 Vip에 접근하여 서비스 다운이 된 것은 맞지만

이 시간이 트래픽이 높은 시간대가 아니고, 심지어 빅스마일데이 때의 트래픽도 버텨냈는데 뭔가 이상하다는 생각이 들었습니다.

 

답은 봇의 특성과 DNS, 글로벌 로드 밸런싱 간에 있었습니다.

이해를 돕기 위해서는 DNS(Domain Name System)에 대한 설명이 필요할 듯합니다.

DNS는 도메인 이름을 보내면 매치되는 ipAddress 주소를 알려주는 시스템입니다.

 

 

이를 통해 사용자는 숫자로 되어있는 ipAddress 주소를 외울 필요가 없어지며 상대적으로 외우기 쉽고 소통하기 쉬운 문자로 된 도메인 이름을 사용할 수 있습니다.

 

L7(로드밸런서)의 분산 알고리즘 중 라운드 로빈방식은 이를 활용한 것입니다.

 

하나의 서비스를 운영하는 분산된 각 서버의 ipAddress 주소를 DNS에 하나의 도메인 이름으로 등록하면 클라이언트는 DNS에 해당 도메인 이름으로 요청할 경우 등록된 각 서버의 ipAddress주소를 순환하여 반환하는 방식입니다.

 

하지만 이 방식에는 문제점이 여러 가지가 있는데요.

 

그중 하나가 DNS는 같은 클라이언트에 대해 한번 접속한 ipAddress주소를 일정 시간 동안 캐싱하기 때문에 일부 서버에 트래픽이 과도하게 집중될 수 있다는 것입니다.

 

ex)

정상 유입 -> GSLB -> 데이터 센터 트래픽 균등 분배

봇 유입 -> GSLB -> 동일 세션 정보로 데이터 센터 A로 판단 -> 데이터 센터 A 유입 -> 서비스 다운

 

정상적인 유입이라면 데이터 센터 서버군들이 요청을 균등하게 분배받아 처리를 해야 하는데

한쪽 서버군으로만 트래픽이 몰리니 발생한 현상입니다.

 

빠르게 방향을 전환해서

봇이 정말 ipAddress가 무제한일까?

저는 반드시 동일한 ipAddress가 N초 안에 올 것이라고 가설을 세웠습니다.

 

이를 증명하기 위해 장애가 발생한 시점에 로그를 추출하였고, 그 로그에 있는 ipAddress를 전부 추출하였습니다.

(생일이라 휴가를 내었지만 휴가를 반납하며 이 작업을 하면서 눈물 흘렸습니다..)

다시 추출된 ipAddress를 가지고 하나씩 검색을 했습니다.

아래와 같이 해당 ipAddress가 몇 초 안에 몇 번의 요청이 오는지 집계하였습니다.

 

ipAddress ?초 ~ 0.1초 0.2초 0.3초 0.4초 0.5초 0.6초 1초 ~ 10초 ~ 1분 ~ 10분 ?분
1.1.1.1 1 ~ 1 4 8 10 12 14 16 ~ 160 ~ ~ ~ ~ ~
2.2.2.2                             1 ~
3.3.33                             1 ~
4.4.4.4 2 ~ 2 3 5 8 15 30 100 ~ ~ ~ ~ ~ ~ ~
5.5.5.5 4 ~ 4 5 9 10 12 15 30 ~ ~ ~ ~ ~ ~ ~
~     ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

 

작업을 하다 보니 정상적인 고객이 보였습니다.

이제 가설에 대한 증명이 되었고 어떻게 봇을 판단할 것인지는 아래와 같이 정하였습니다.

정상적인 고객이 아닌 봇이라면 N초 안에 M번의 요청이 오면 X분간 차단한다.

(위에서 설정한 Threshold는 운영을 하며 유동적으로 변경할 예정입니다.)

 

위 UserAgent 기반의 차단 방법에 데이터만 ipAddress로 변경하였습니다.

 

Ip를 차단시키는데 정말 들어와야 하는 봇은 차단하면 안 되기 때문에 WhiteList를 만들었습니다.

구글 봇과 빙 봇들은 공식 홈페이지에서 봇에 대한 ip 대역을 제공해주고 있어서 수월했으며

광고에 필요한 봇들은 협력 업체에 Ip대역을 제공받았습니다.


진짜 끝으로

우선 이 현상을 파악하기 위해 도움 주신 모든 분들께 감사의 말씀드립니다.

 

지금과 같은 방식으로 차단을 하여도 또한 봇들은 기술적으로 진화하여 또 다른 방식으로 찾아올 것입니다.

근본적으로 제가 봇을 차단하는 담당 팀은 아닙니다.

 

제가 해당 기능을 평일 주말 밤 낮 없이 한 달이 넘는 기간 동안 눈물을 쏟아가며 가설을 세우고 증명하고 개발한 것은

제가 담당하는 도메인이 무너지는 것을 손 놓고 바라볼 수 없었기 때문에 최소한의 방어라도 하겠다는 의지였습니다.

 

이 글이 노출되는 지금 시점에는 저는 IpAddress가 다양하게 들어와도 탐지해 내는 알고리즘을 만들었고

지속적으로 악성 봇들의 행동 패턴을 분석하고 그에 맞춰서 탐지 & 차단하도록 개발하고 있습니다.

기능이 점점 커지며 시스템이 되자 악성 봇의 위협을 불태워버리겠다는 의미로

인페르노 [지옥불] 이라는 이름을 지었습니다.

 

개발을 하다 보면 정말 한 치 앞도 알 수 없는 일들이 발생하는 것 같습니다.

때론 탐정이 되는 것 같기도 순간이 스펙타클 할 때도 끝이 보이지 않을 것 같은 고통 속에 있는 때도 있지만

해결하게 되면 짜릿합니다.

아마 이런 맛에 개발을 하는 게 아닌가 싶습니다.

 

주니어 개발자에서 어느덧 주니어 개발자가 돼 가는 시기에 많은 이슈들이 발생하고 있어

때로는 원망스럽기도 하다가도 저를 더욱더 강인하게 담금질 해주기에 감사하게 생각하고 있습니다.

더욱 더 좋은 개발자가 되기 위하여 끊임없이 노력하겠습니다.

 

긴 글 읽어 주셔서 감사합니다.

 

현재 위 내용과 별개로 지마켓에는 여러 기술이 시험되고 적용되어 서비스와 시스템을 보호하고 있습니다.

댓글