최근에 웹소켓 서비스쪽 작업중에 이상한 문제가 발생했다. 문제가 없던 웹소켓 서버에 접속이 안되는 문제가 발생한 것이다. 새로고침도 여러번 하고 브라우저도 바꾸어보아도 여전히 접속이 불가능했다. 지인분들을 통해 나에게만 발생하는 문제임을 확인했다. 그제서야 뭔가 문제가 있음을 느끼고 무슨일이 일어나고 있는지 살펴 보았다.

WS 요청을 했는데 200이 온다고?

가장 먼저 개발자 도구를 열어보았다. 개발자 도구에서 네트워크 패널을 보면 브라우저가 어떻게 통신을 했는지 간단하게 볼 수 있다. 네트워크 패널을 이용하여 웹소켓 요청을 찍어봤는데, 응답 코드로 101이 아닌 200이 오는것을 확인하였다.

WS upgrade를 요청하면 101이 나와야 함이 정상인데 200이 온 것이다. 해당 웹소켓 서버는 내가 제작을 한 것이었는데, 기억에 분명 200을 리턴하는 코드는 작성한 적이 없었다. 혹시 특정 상황에서 내가 200을 리턴하도록 한 것이 있나? 해서 살펴 봤는데 전혀 없었다. 또한 저런 응답헤더를 찍어내는 코드는 전혀 없었다.

새로고침을 몇번 해도 200 결과만 나온다. (웹소켓 요청만 필터링 한 것이다)

웹소켓 백엔드 서버에서 발생하는 문제는 아닌것으로 판단하고, 그 앞단을 살펴 보기로 했다. 이 서버는 [유저 -> CDN -> WS서버] 의 접속 구조를 가지고 있다. WS서버의 문제는 아닌것을 확인했으니 CDN을 파 보아야 할 차례였다. 빠르게 해당 CDN을 경유하여 WS를 접속하는 다른 사이트를 찾아보았다. 

결과는 정상이었다. 몇번을 시도해 보았고, 수신지 IP가 동일함 까지 확인해 보았다. 테스트 도중 위의 요청 헤더가 이상함을 한번 더 확인하였다. CDN 에서 요청을 내려줬으면 CDN 특유의 헤더가 찍혀야 하는데, 그런것이 전혀 없다. 일단 Response가 나온다는거면 아무리 CDN이 꼬여도 특정 헤더 하나정도는 찍혀야 마땅하다. 즉, 하나 정도는 묻혀 나와야 할 헤더가 없던 것이다. (예를 들어, Cloudflare라면 server: cloudflare 등의 헤더가 있어야 한다)

사실 처음에는 CDN 내부에서 오류가 발생했거나, GET / 요청이 캐싱되어서 GET ws://~~~/ 에서 캐싱된 200 페이지가 나오는것으로 생각했었다. 근데 그것도 아님이 확인되었다. 이제 내 PC에서 CDN으로 가는 사이에서 무슨 일이 벌어지고 있다고 생각했다. 가장 먼저 내 PC에 바이러스나 악성 프로그램이 설치되어 MITM 같은 무언가가 일어나고 있다고 판단했다.

비슷한 환경의 사이트들을 접속해 보면서 여러 테스트를 해 보았다. 그냥 GET으로 접속하면 아무런 문제가 발생하지 않았다. 또한, wss로 접속하면 문제 없이 접속 가능함을 확인 할 수 있었다. 그래서 “다른 웹소켓 서버에 wss가 아닌 ws로 접속을 시도해 보자!” 를 위주로 테스트 해 보았다. 결과적로 저 도메인에서만 문제가 발생함을 확인할 수 있었다.

그냥 HTTP로 접속하면 정상으로 404가 나왔다.
다른 도메인으로의 ws 연결은 정상이었다.

일단 내 컴퓨터에서의 문제는 아닌것 같아서 MITM의 실체를 파악해 보기 위해 Wireshark로 패킷을 찍어보았다. 그리고 빠르게 패킷을 Follow 해 보았다. 그러자 실체가 들어났다.

일종의 MITM 공격이 맞았다. 패킷을 정보를 보아하니 내 PC에서 급조된 것도 아니었다. Wireshark로 찍어보니 비로소 body를 볼 수 있었다. (개발자 도구의 네트워크 패널에서는 websocket 요청일 경우 Response body 대신에 WS 전용 메세지 뷰어가 보인다.) 저 IP에 대해 파보았더니, KT의 PC접속 대수 제한 시스템이 나왔다. 그렇게 1차 결론이 나왔다.

1차 결론: KT의 PC접속대수 파악 시스템으로 인해 WS 요청이 막혔다.

이 문제는 KT의 PC 접속댓수 시스템이 요청을 하이제킹 하는 과정에서 접속 장애가 발생한 것이다. 나 같은 경우에는 실제 PC접속 대수 제한이 걸린것은 아니다. PC 접속대수를 파악하는 악성(?) 스크립트가 하필이면 웹소켓 요청에 끼어 들어와서 정상적인 웹소켓 접속이 불가했던 것이다. 이게 웹소켓이 아니었으면 잠시 흰색 페이지가 표시되다가 페이지가 제대로 열려서 눈치 채지 못했겠으나, 하필이면 웹소켓이어서 이런 문제가 발생한 것이다.

조금 더 파 보기로 했다. 일단 MITM으로 들어온 HTTP 요청을 보아하니 악성(?) 스크립트를 실행하고 2초 내로 원래 페이지로 돌아가게 처리되어 있었다. 아무래도 일반 유저들이 PC접속 대수 제한 시스템이 뒤에서 돌아가는가 알 지 못하게 하려고 한 것 같다. 나였어도 2초후에 페이지가 제대로 표시되면 문제가 있는지 알아체지 못했을 거다.

원래 정상 요청은 어디로 갔을까? Wireshark를 찾아보면 알 수 있다. (후설 하겠지만) 이 MITM은 KT 백본측 장비에서 악의적으로 위조한 패킷을 집어넣어서 발생한 것이다. 이 사실은 클라이언트(PC)와 서버측 모두 알 수 없다. 그렇기에 서버 측에서는 계속해서 “클라이언트가 패킷을 못 받았나?”라고 생각하고 re-transmission 한다. 그러나 클라이언트에서는 이미 KT의 위조 패킷에 의해 ACK 카운트가 올라가서 “이거 패킷이 이상한데?” 라며 패킷을 버린다. 

인터넷은 실제로는 일종의 “편지”인 “패킷” 단위로 동작한다. “편지”가 잠시 오배송 되거나, 순서가 뒤바뀌어 어제 보낸것 보다 오늘 보낸것이 더 빨리 도착할 수도 있다. 그렇기에 모든 패킷에는 전송 번호를 부여하여 “어떤 순서로 발송했는지”를 추적한다. 근데 KT의 장비가 “다음 전송은 이거임!” 이라면서 허위로 패킷을 전송한다. KT가 발송순서를 위조했기에 PC에서는 정상 패킷을 오히려 이상한 패킷으로 판단한다. “엥? 이상한 편지가 왔는데?” 하면서 편지를 버리는 것이다. 위 캡쳐의 검은색 부분이 “비정상적인 패킷으로 판단하여 버려진” 것들의 목록이다.

서버는 그저 아무것도 모르고 중간에 패킷이 드롭된 줄 알고 계속해서 재 전송을 한다.

어떻게 보면 “TCP의 연결성”과 “TCP Retransmission”을 볼 수 있는 재미있는 자료이다. 

상담사를 통한 1년 시스템 개입 연기

일단 당장 웹소켓에 접속이 되지 않아서 KT 고객센터에 전화하였다. 나 같은 경우에는 실제 차단이 된 경우는 아니다. 그러나 PC접속대수 시스템에 의해 인터넷(웹소켓) 사용에 장애가 있어서 “PC접속대수 장애 안내 페이지”의 고객센터로 전화하였다. 뭐… 이런저런 이야기를 했는데 결과적으로 “MAC 주소로 차단하는것은 아니며, User-Agent나 물리적 Port를 체크하는 방식도 아니다” 는 것을 확인했다. 그리고 사정을 설명하니까 금방 차단 해제가 되었다. 상담사 말로는 최대 1년동안 이 페이지가 안나오게 할 수 있다고 한다. 그렇게 1년동안 페이지가 나오지 않도록 조치 받았다.

나는 집에 설치된 PC도 애초에 랩탑과 PC 두대 밖에 없고 아마 개발용 VM을 사용해서 오탐이 됐을거라 주장했다. 더 나아가 애초에 나는 차단을 당한 상황도 아니고, 이것으로 인해 정상 서비스(웹소켓)의 사용에 장애가 발생함을 알려줬다. 이런 상황을 종합 판단하여 상담사분이 1년동안 시스템이 개입하지 않도록 조치해 줬다. (실제로도 집에 PC와 랩톱 2개밖에 없다.) 

어짜피 KT 서버에 실제 PC 접속정보가 남아있을거다. 추후 분쟁을 방지하기 위해 차단 근거를 남겨놓았을것이 당연하다. (뒤에서 언급하겠지만) 수집하는 정보에 기기 정보와 그래픽카드 정보가 포함되어 있으므로 상담사가 “이 고객은 정말 2PC인데 억울하게 걸렸는지, 아니면 허위로 2PC만 사용한다고 말을 하는건지”를 판단할 수 있다. 패드류를 데스크탑 모드로 사용하여 오탐이 된 것이면 상담사가 기록을 보고 처리 해 줄 것이다. (그래픽카드 정보등이 있기 때문에 단번에 패드임을 구분할 수 있을것이다.) 3대 이상의 PC를 사용하는데 혹시 차단 해제가 가능할까..? 하고 떠보는것은 어려울 것이다.

이왕 걸린김에.. KT는 어떻게 접속대수를 파악할까?

MAC주소도 아니고 UA도 아니고 어떻게 판단을 할까? 아는 형님의 도움을 받아서 파 보았다. 토큰이 유효할 때 Wireshark에 나온 JS가 있는 페이지를 열면 다음과 같은 내용을 볼 수 있다:

이 페이지는 무엇을 할까? 결과적으로는 사용자의 PC정보를 캐내는 것이다. 자세한 내용은 https://kimtruth.github.io/2020/02/29/Big-K-is-watching-you/ (K사 추가단말서비스 가입안내 분석) 에서 볼 수 있다. 결국 사용자의 PC에서 Finger Print를 수집하여서 PC의 고유값을 매기는 식이다. Canvas를 이용한 Finger Print 기술등을 알고는 있었지만 실제로 저렇게 사용하는곳은 처음 본 것 같다.

이것을 정리해 보면… 사용자는 눈치채기 어렵지만 KT는 “PC라고 판단된 장비”에 대해서 장비 정보와 고유값을 캐내어 관리한다는것을 알 수 있다. 또한 Warning.or.kr등의 특수 목적이 아님에도 (사적인 목적으로) MITM 기법을 이용하여 HTTP 요청을 하이제킹 한다는것도 볼 수 있다. 

Warning.or.kr은 그래도 (논란은 있지만) 공적인 목적에다가 사용자의 정보를 수집하는것은 없다. 그리고 사용자에게 하이제킹 했다는 피드백도 분명하게 해 준다. 그것에 비해 이 방식은 사용자 몰래 사적인 목적으로 MITM을 함은 물론이고 PC정보까지 수집한다는 점에서 문제가 있다.

이와 관련해서 아는 형님분하고 이야기 해 보았는데, 80번 포트 뿐 아니라 8080 포트등에서도 발동할 수도 있다고 했었다. 8080 포트로 http 요청을 했는데 접속이 안돼서 보아하니 마찬가지 페이지가 뜬 경험이 있다고 하였다. 만약 그냥 HTTP만으로 API등을 구축해야 한다면, 범용적이지 않은 포트를 이용하자.

다른 분들과 대화하며 이 시스템 개입시간도 알 수 있었고, 그 이유도 유추할 수 있었다. 차단 시스템이 동작하는 시간은 “상담사가 상주하고 있는 시간” 임을 확인한 것이다. 지금 내 사례와 같이 아무 문제가 없는데 오탐 또는 탐지 과정으로 인해 장애를 호소하는 고객이 있을 수도 있다. 이 경우에는 정상유저의 사용을 이유없이 방해한 것이 된다. 이런 경우를 대비해서 상담사가 대기하는 동안에만 작동하도록 했을거라 유추했다.

한편, 상담사분과 통화하면서 걸리는 조건과 발동 조건에 대해 귀띔을 받긴 했다. 그러나 1:1 통화 내용을 여기에 올리기는 좀 그래서… 혹시 페이지가 나왔는데 발동 조건을 알고 싶은 사람이 있으면 저기 상담 센터로 전화 해 보자. 친절하게 안내 가능한 선에서 설명해 주신다.

MITM을 조금 더 깊게 파보자

위에서 KT가 뒤에서 무슨짓을 벌이는지 알 수 있었다. 이번에는 옆을 파보자. 내가 접속을 시도했던 IP는 115.139.128.11이다. 이를 tracert로 찍어보면 아래와 같이 나온다:

그리고 KT에서 끼워 넣은 패킷을 보면 아래와 같은 정보를 볼 수 있다:

그리고 이것은 정상적으로 서버에서 보낸 패킷이다:

여기서 볼 수 있는 특이점은 KT에서 보낸 패킷은 TTL이 하나 더 많다는 것이다. 패킷의 Timestamp를 보아도 공유기 내에서 발생했다거나, 서버에서 발신된 패킷을 참고하여 만들었다고 하기 어렵다. KT 공유기에서 접속수를 판단한다기 보단, 백본에 붙어있는 장비가 DPI를 했다고 생각함이 더 옳을것 같다.

결론은..?

HTTPS를 꼭 쓰자. (최소한) KT는 평문 HTTP 요청에 대해선 DPI를 이용해서 감시하고 있으며, 사용자 몰래 장비의 finger print까지 수집한다. 사용자는 자신의 finger-print가 밖으로 나가는지 알 수 없다. 게다가 수동적인 감시에 그치지 않고 직접 패킷을 삽입하는등 상당히 적극적인 모습까지 보여준다. 자신의 정보가 이런식으로 몰래 빼돌려지는것을 좋아하는 사람은 없을것이다.

또한, HTTP페이지의 무결성에 대해 확신하면 안된다. 위와 같이 몰래 패킷을 집어넣으면 사용자는 알 수가 없다. 물론 그런식으로 악랄한 시스템까지 구축했을거란 생각이 들진 않지만, 하려면 할 수 있을거다는것도 사실이다. 상담사를 통해 요청하면 같은 회선에 대해서는 이 페이지가 안 나오게 할 수 있다는것은, 반대로 말하면 “특정 회선에만 DPI 룰을 적용 할 수 있음”을 보여준다.

개발자라면 API를 태우거나 REST를 태우거나 하면 HTTP대신 HTTPS를 쓰자. 여건상 HTTPS에 태우기 어려운 상황이라면 포트라도 범용적이지 않은것으로 바꾸자. 패킷을 감시·도청되는것이 직접적인 문제가 아니다. MITM때문에 끼워넣어진 패킷 때문에 프로그램이 비정상 적으로 작동할 수 있다. 

if (response.code == 200) { // 정상처리 }

이런식으로 코딩했다가는 훅 갈 수도 있다. 게다가 finger print를 제출하지 않으면 1시간 넘게 문제가 발생할 수도 있다. 나 같은 경우에도 최초 발견이 10시 8분인데 상담사에게 통화하고 ws 요청 코드가 101이 표시된것을 확인한게 10시 57분이다. 한번 굴레에 빠져버리면 상담사하고 연락하기 전까지는 아예 먹통이 될 수도 있다.

발동 조건을 외부에서 알기 어렵다. 이번 같은 경우에도 ws 접속 전용 도메인에서 발생했다. 당신이 만든 프로그램이 간헐적으로 접속이 안된다고 클레임이 들어왔는데 사실 이런 상황 때문이라면? 그냥 답이 없다. 개발자 측에서는 “문제가 없다”고 할 수 밖에 없다. 사용자 입장에서도 바이러스나 그런것도 아니기에 “개발사”에게 따지는 수 밖에 없다. 특히 임베디드 기계라면? 저런것에 대한 대응이 전혀 안되어 있을텐데 몇시간동안 장비가 정지된 상태로 있을수도 있다. 대응도, 파악도 사실상 불가하다. (사용자에게 Wireshark를 깔라고 할것인가..?)

위에 언급 했듯 차단 시스템은 특정 시간대에서만 동작한다. 밤 10시에 오탐지나 나와 같은 상황이 발생했을때 클래임을 걸 수 있는곳이 없지 않는가? 혹시 웹소켓요청(또는 비 암호화 HTTP 요청)을 하는데 알 수 없는 장애가 발생하면서 + 09~18시 동안만 장애가 발생하며 + KT를 사용한다면 차단 시스템에 의한 오작동을 의심해 봐야 할 것이다.