치지직 방송 알림을 디스코드 채널에 전달하는 서비스에서 보안 취약점이 악용되어 일부 디스코드 서버에 대량 멘션이 발생했습니다. 이번 사건은 소규모 사이드 프로젝트라도 사용자가 늘어나면 실제 서비스와 동일한 수준의 보안 관리가 필요하다는 사실을 보여주었습니다. 이 글에서는 서비스 개발자의 관점에서 사고가 발생한 배경과 시간대별 대응 과정, 로그 분석 방법, 보안 패치 및 이후의 개선점을 살펴봅니다.
치직은 어떤 서비스이며 보안 사고는 어떻게 발생했을까요?
치직은 치지직에서 스트리머가 방송을 시작하면 미리 설정된 디스코드 채널에 알림 메시지를 보내는 서비스입니다. 사용자가 웹 대시보드에서 서버와 알림을 관리할 수 있도록 설계되었으며, 초기에는 기술을 실험하기 위한 개인 프로젝트로 시작했습니다.
치지직 방송 알림을 디스코드로 전달하는 구조
치직은 서버리스 아키텍처를 활용해 방송 알림과 관련된 기능을 처리합니다. 사용자는 웹 대시보드에서 디스코드 서버를 선택하고 알림을 설정할 수 있으며, 설정된 조건에 따라 방송 시작 메시지를 전송합니다.
프로젝트 초기에는 DynamoDB를 비롯한 기술을 직접 사용해 보는 것이 주된 목적이었습니다. 서비스를 실제로 이용하는 사용자가 생기면서 관리 기능이 추가되었지만, 처음부터 대규모 서비스로 성장할 것이라고 예상하지는 못했습니다.
이러한 개발 배경은 보안 설계에도 영향을 주었습니다. 초기에는 다른 서버에 알림 설정을 추가하는 행위 자체가 큰 피해로 이어질 가능성이 낮다고 판단했습니다. 그러나 이후 커스텀 메시지와 테스트 알림 전송 기능이 도입되면서, 권한 검증이 충분하지 않은 상태에서 발생할 수 있는 위험이 커졌습니다.
서버 관리 권한을 확인하지 않은 API의 문제
사고의 핵심 원인은 웹 대시보드에서 특정 디스코드 서버의 설정을 변경할 때 사용자에게 해당 서버를 관리할 권한이 있는지 제대로 확인하지 않았다는 점입니다.
치직의 대시보드는 다음과 같은 URL 구조를 사용했습니다.
https://chzzk.junah.dev/server/{서버ID}
이 주소에서 서버 ID를 변경했을 때, 해당 서버의 관리 권한이 없는 사용자도 관련 기능에 접근할 수 있는 문제가 있었습니다. 특히 커스텀 메시지를 등록하거나 테스트 알림을 전송하는 기능이 악용될 경우, 타인의 디스코드 서버에 원하지 않는 메시지를 보낼 수 있었습니다.
중요한 점은 서버 ID를 알고 있다는 사실과 해당 서버를 관리할 권한이 있다는 사실은 전혀 다르다는 것입니다. 웹 페이지에서 접근을 제한하는 것만으로는 충분하지 않으며, 실제 API 요청을 처리하는 서버에서도 권한을 검증해야 합니다.
오전 9시 23분, 대량 멘션 제보로 사고를 인지했습니다
사고 당일 개발자는 가족과 여행 중이었으며, 여행 마지막 날 호텔 조식을 먹고 공항으로 이동할 예정이었습니다. 오전 9시 10분쯤 일어나 휴대전화를 확인하던 중 치직 서포트 서버와 개인 메시지를 통해 봇이 일부 디스코드 서버에서 @everyone 멘션을 반복하고 있다는 제보를 받았습니다.

슬래시 명령어가 아닌 웹 대시보드를 의심한 이유
처음 전달받은 제보에서는 관리자 권한이 없는 사용자도 슬래시 명령어를 실행할 수 있는 문제가 원인으로 지목되었습니다. 하지만 첨부된 화면에는 사용자가 직접 설정한 것으로 보이는 커스텀 메시지가 포함되어 있었습니다.
당시 치직의 슬래시 명령어에서는 커스텀 메시지를 등록할 수 없었습니다. 따라서 단순히 디스코드 명령어의 권한 설정이 잘못된 문제라기보다는, 웹 대시보드와 연결된 API의 권한 검증에 문제가 있을 가능성이 높다고 판단했습니다.
또한 공격이 치직의 정상적인 방송 알림 기능을 통해 이루어지고 있다는 점도 중요한 단서였습니다. 일반적으로 디스코드 봇이 악용되는 사고에서는 봇 토큰 유출을 의심할 수 있지만, 이번 사건은 웹 대시보드의 기능을 비정상적으로 호출하는 방식으로 발생한 것으로 보였습니다.
오전 9시 32분, 모바일 환경에서 긴급 차단 조치
당시 호텔 조식 입장 시간이 얼마 남지 않았기 때문에, 가족과 함께 식당에 들어간 뒤 휴대전화로 긴급 대응을 진행했습니다.
우선 AWS 웹 콘솔에 접속해 메시지 전송 기능을 담당하는 두 Lambda 함수의 API Gateway 트리거를 제거했습니다. 대상은 테스트 메시지 전송 Lambda와 방송 알림 전송 Lambda였습니다. 이어서 디스코드 개발자 포털에서 치직 봇의 토큰도 재설정했습니다.
현재 시점에서 돌아보면, 메시지 전송 API의 트리거만 차단해도 해당 경로를 통한 공격을 막을 수 있었을 가능성이 높습니다. 그러나 당시에는 공격 경로와 범위를 정확히 파악하지 못했기 때문에 여러 조치를 동시에 적용했습니다.
토큰 재설정에는 부작용도 있었습니다. 치직과 디스코드 사이의 인증 정보가 변경되면서 서버 목록을 불러오는 기능을 비롯해 디스코드 연동이 필요한 여러 기능이 정상적으로 작동하지 않았습니다. 웹 대시보드에 로그인하더라도 서버 목록이 표시되지 않아 다른 기능을 이용하기 어려운 상태가 되었습니다.
이 경험은 긴급 대응 과정에서 공격 차단뿐 아니라 서비스 복구에 필요한 절차도 함께 고려해야 한다는 점을 보여주었습니다.
오전 9시 40분, 실제 취약점과 설계상의 문제를 확인했습니다
사고가 발생한 뒤 웹 대시보드의 구조를 다시 살펴보면서, 서버 ID를 직접 변경하는 방식으로 다른 서버의 설정 페이지에 접근할 수 있다는 사실을 확인했습니다.
초기 개발 단계에서 놓친 권한 검증
이번 취약점은 개발 과정에서 전혀 예상하지 못했던 문제가 아니었습니다. 초기 구현 당시에도 일부 API에 서버 관리자 권한을 확인하는 로직이 없다는 점을 알고 있었습니다.
다만 프로젝트의 출발점이 기술 실험이었고, 처음에는 다른 서버에 방송 알림을 추가하는 행위가 심각한 문제를 일으킬 것이라고 생각하지 않았습니다. 커스텀 메시지 등록과 테스트 알림 전송 기능이 없었던 시기에는 이러한 접근이 악용될 가능성을 상대적으로 낮게 평가했습니다.
하지만 서비스 기능이 늘어나면 기존에 위험하지 않다고 판단했던 접근 방식도 새로운 보안 위협으로 바뀔 수 있습니다. 특히 메시지 내용과 전송 시점을 사용자가 제어할 수 있게 되면, 단순한 설정 변경 권한 누락이 대량 메시지 전송이나 멘션 도배로 이어질 수 있습니다.
기능을 추가할 때는 기존 권한 모델이 새로운 기능에도 적절한지 다시 검토해야 합니다. 과거에 문제가 없었다는 사실은 현재의 보안을 보장하지 않습니다.
서포트 서버에 사고 공지를 게시한 과정
개발자는 사용자들에게 상황을 알리기 위해 치직 서포트 디스코드 서버에 공지를 게시했습니다. 평소 불필요한 @everyone 멘션을 사용하지 않았지만, 이번에는 다수의 사용자에게 긴급 상황을 전달할 필요가 있다고 판단해 해당 멘션을 활용했습니다.
당시에는 이동 중인 모바일 환경에서 공지를 작성했기 때문에 평소 사용하던 공지 형식과 차이가 있었고, 일부 오타도 포함되었습니다. 이후 공지 내용을 수정했습니다.
긴급 상황에서는 신속한 안내가 중요하지만, 공지에는 확인된 사실과 아직 조사 중인 내용을 구분해 담는 것이 좋습니다. 또한 사용자가 취해야 할 조치와 서비스 복구 상황을 명확하게 전달하면 혼란을 줄이는 데 도움이 됩니다.

AWS CloudWatch 로그 분석으로 공격 범위를 파악했습니다
여행 일정 때문에 즉시 코드 수정과 재배포를 진행하기는 어려웠습니다. 대신 모바일 환경에서 AWS CloudWatch Logs Insights를 이용해 요청 기록을 분석했습니다.
오전 6시부터 증가한 Lambda 호출 기록
치직 서포트 서버에서는 여러 사용자가 이번 사건에 관해 이야기하고 있었습니다. 취약점이 외부에 공유되어 여러 사람이 공격에 참여했을 가능성과, 치직 봇이 설치된 대규모 서버를 대상으로 금전적인 보상을 제안했다는 이야기까지 나왔습니다.
이러한 제보가 모두 사실이라고 단정할 수는 없었지만, 피해 범위를 확인하기 위해 테스트 알림 전송 Lambda의 호출 기록부터 살펴봤습니다.
AWS 모니터링 화면을 확인한 결과, 오전 6시부터 긴급 조치를 진행하기 전인 오전 9시까지 함수 호출이 평소보다 크게 증가했습니다. 이를 통해 공격이 오전 6시 무렵부터 시작되었을 가능성을 파악했습니다.
API 로깅이 사고 원인 분석에 도움이 된 이유
다행히 사고 발생 약 20일 전에 모든 API 요청에 대한 로깅을 추가해 둔 상태였습니다. 덕분에 단순히 호출량이 증가했다는 사실을 확인하는 데 그치지 않고, 어떤 계정에서 요청이 발생했는지 추가로 분석할 수 있었습니다.
CloudWatch Logs Insights에서 해당 시간대의 기록을 조회한 결과, 총 4개의 계정이 요청을 보낸 것으로 확인되었습니다. 그중 한 계정이 전체 요청의 대부분을 차지했습니다. 확인된 기록에서는 총 897건의 요청 가운데 884건이 해당 계정에서 발생했습니다.
이처럼 요청 시각, 호출 횟수, 사용자 식별 정보 등을 함께 기록하면 비정상적인 트래픽 패턴을 분석하는 데 도움이 됩니다. 다만 로그에 포함되는 정보는 필요한 범위로 제한하고, 접근 권한과 보관 기간도 적절히 관리해야 합니다.

OAuth 토큰과 IP 기록을 통한 추가 조사
치직은 별도의 사용자 계정 정보를 직접 저장하는 대신, 디스코드 OAuth2 인증 과정에서 제공받은 토큰을 사용하고 있었습니다. 해당 토큰으로 사용자 정보를 조회해 공격 요청에 사용된 계정의 디스코드 ID와 사용자명 등의 정보를 확인할 수 있었습니다.
그러나 이메일 주소는 개인정보 보호 처리로 인해 활용할 수 있는 정보가 제한적이었고, 계정 역시 부계정으로 추정되는 정황이 있었습니다.
요청 기록에서 IP 주소도 조회했습니다. 총 3개의 IP 주소가 확인되었지만, VPN을 이용한 것으로 보이는 주소들이어서 IP 정보만으로 사용자를 특정하기는 어려웠습니다.
이후 가장 큰 피해를 입은 서버의 관리자와 관련 로그를 확인하는 과정에서 추가적인 단서를 확보했고, 이를 바탕으로 공격자를 특정할 수 있었습니다. 다만 이 과정은 단일 로그나 IP 주소만으로 신원을 확정한 것이 아니라 여러 정황을 종합해 분석한 결과였습니다.
이번 경험은 로그를 남기는 것뿐 아니라 사고 발생 후 활용할 수 있는 형태로 기록을 설계하는 것이 중요하다는 사실을 보여주었습니다.

오후 10시 17분, API 권한 검증을 보강하고 서비스를 복구했습니다
공격 경로와 취약점의 원인을 파악한 뒤에는 서버 측 권한 검증 로직을 개선했습니다. 긴급 대응 과정에서 비활성화했던 기능을 복구하기 전에 보안 패치를 적용하고, 정상적인 사용자만 관련 API를 호출할 수 있도록 수정했습니다.
공통 미들웨어에서 관리자 권한을 확인하도록 개선
기존에는 일부 API에서 서버 관리자 권한을 확인하지 않았습니다. 이를 개선하기 위해 여러 요청에서 공통으로 실행되는 미들웨어에 관리자 권한 검증 코드를 추가했습니다.
또한 각 Lambda 함수에서 관리자 권한 검사를 적용할지 지정할 수 있도록 구조를 변경했습니다. 이를 통해 권한 검증이 필요한 기능을 명확히 구분하고, 개별 API 구현 과정에서 검사를 빠뜨릴 가능성을 줄이고자 했습니다.
다만 공통 미들웨어에 검증 로직을 추가하는 것만으로 모든 보안 문제가 해결되는 것은 아닙니다. 각 요청에서 인증된 사용자가 누구인지 확인하고, 해당 사용자가 요청 대상 서버를 실제로 관리할 권한이 있는지 검증해야 합니다. 관리자 권한이 필요하지 않은 공개 기능과 관리 기능을 구분하는 것도 중요합니다.
API Gateway 트리거 복구와 봇 토큰 재생성
권한 검증 코드를 수정한 뒤에는 제거했던 Lambda 트리거를 복구하고, 디스코드 봇 토큰을 새로 생성해 서비스를 재배포했습니다.
이 과정에서는 단순히 API가 호출되는지만 확인하는 것이 아니라, 정상 사용자와 권한이 없는 사용자의 동작을 구분해 테스트하는 것이 중요합니다. 특히 다른 서버의 ID를 입력했을 때 접근이 차단되는지, 기존 알림 설정과 서버 목록 조회가 정상적으로 작동하는지 확인해야 합니다.
오후 11시 38분, 추가 테스트를 거쳐 사고 공지를 마무리했습니다
보안 패치와 서비스 복구 작업을 진행한 뒤 추가 테스트를 수행했습니다. 이후 사용자들에게 사고 경위와 대응 내용을 안내하는 공지 겸 사과문을 게시했습니다.
피해를 입은 사용자에게 책임을 설명하는 방법
이번 사고로 실제 피해를 입은 디스코드 서버가 존재했기 때문에, 단순히 취약점이 수정되었다는 사실만 전달하는 것으로는 충분하지 않았습니다.
개발자는 권한 검증을 제대로 구현하지 못한 자신의 책임을 인정하고, 사고에 대한 사과와 함께 조치 내용을 설명했습니다. 책임을 회피하는 표현을 피하고, 서비스 운영자로서 부족했던 부분을 명확하게 전달하는 데 초점을 맞췄습니다.
보안 사고 공지에서는 확인된 사고 원인, 영향 범위, 적용한 조치, 추가적인 점검 계획을 구분해 안내하는 것이 좋습니다. 아직 확인되지 않은 사실을 단정하거나 근거가 부족한 추측을 전달하면 오히려 혼란을 키울 수 있습니다.
또한 사과문은 사고를 마무리하기 위한 형식적인 절차가 아니라, 사용자에게 발생한 피해를 인정하고 향후 개선 방향을 설명하는 과정이어야 합니다.

이번 보안 사고를 통해 배운 세 가지 교훈
치직은 수익을 목적으로 운영하던 프로젝트가 아니었고, 개발 과정에서 운영을 중단할지 고민하기도 했던 사이드 프로젝트였습니다. 그럼에도 사용자가 늘어나면서 실제 서비스와 마찬가지로 공격의 대상이 될 수 있었습니다.
이번 사건을 통해 가장 크게 느낀 점은 보안 취약점을 알고 있다는 것과 이를 실제로 해결하는 것은 전혀 다르다는 사실입니다.
첫째, 작은 프로젝트에도 서버 측 권한 검증이 필요합니다
서비스 규모가 작거나 기능이 단순하다는 이유로 권한 검증을 미루면 안 됩니다. 특히 사용자 입력으로 서버 ID, 리소스 ID 등을 전달받는 API에서는 해당 리소스에 대한 접근 권한을 반드시 확인해야 합니다.
웹 화면에서 버튼을 숨기거나 URL 접근을 제한하는 것은 사용자 경험을 위한 조치일 뿐, 보안 경계로 사용하기에는 부족합니다. 권한 검증은 실제 요청을 처리하는 서버에서 이루어져야 합니다.
둘째, 로그는 장애 대응뿐 아니라 보안 사고 분석에도 중요합니다
평소 API 요청 기록을 남겨 두지 않았다면 이번 사건의 시작 시점과 요청 규모, 관련 계정을 분석하기가 훨씬 어려웠을 것입니다.
로그에는 요청 시각, API 경로, 응답 상태, 요청량, 인증된 사용자 식별자 등 사고 분석에 필요한 정보를 적절히 포함할 수 있습니다. 여기에 비정상적인 호출량을 탐지하는 모니터링과 알림을 더하면 공격을 더 일찍 발견하는 데 도움이 됩니다.
다만 인증 토큰이나 비밀번호 같은 민감한 값을 로그에 그대로 기록해서는 안 됩니다. 로그를 통해 원인을 분석할 수 있도록 하되, 개인정보와 인증 정보가 추가적인 위험에 노출되지 않도록 설계해야 합니다.
셋째, 서비스가 성장하면 보안 기준도 함께 강화해야 합니다
초기에는 기술을 실험하는 작은 프로젝트였더라도, 실제 사용자가 생기고 기능이 추가되면 보안 요구사항도 달라집니다.
새로운 기능을 개발할 때는 어떤 사용자가 해당 기능을 호출할 수 있는지, 다른 사용자의 리소스에 접근할 가능성은 없는지, 악용되었을 때 어느 정도의 피해가 발생할 수 있는지 검토해야 합니다.
또한 인증 정보 교체, API 차단, 서비스 복구와 같은 긴급 대응 절차를 미리 준비해 두면 실제 사고가 발생했을 때 불필요한 서비스 중단을 줄일 수 있습니다.
치직의 보안 사고는 더 나은 개발을 위한 경험이 되었습니다
이번 사건은 개발자가 취약점을 인지하고도 위험을 충분히 평가하지 못했을 때 어떤 결과가 발생할 수 있는지 보여주었습니다. 사용자에게 피해가 발생했다는 점은 분명히 아쉬웠지만, 초기 단계의 작은 프로젝트에서 보안 설계와 사고 대응의 중요성을 직접 경험했다는 점에서는 큰 교훈을 얻었습니다.
특히 사용자가 증가하면 서비스에 대한 기대와 책임도 함께 커집니다. 단순히 기능이 정상적으로 작동하는 것만으로는 충분하지 않으며, 권한이 없는 사용자의 접근을 차단하고 비정상적인 동작을 탐지하며, 문제가 발생했을 때 신속하게 대응할 수 있어야 합니다.
앞으로도 치직의 보안 수준을 개선하고, 남아 있는 취약점을 발견하면 지속적으로 수정해 나갈 계획입니다. 프로젝트의 코드와 개선 사항은 GitHub에서 확인할 수 있습니다.
- GitHub 저장소: https://github.com/junah201/chzzk-discord-bot
- 치직 서비스: https://chzzk.junah.dev/
보안 취약점이나 개선이 필요한 부분을 발견했다면 GitHub Issue를 통해 알려주시기 바랍니다. 실제 운영 환경에서 얻은 경험을 바탕으로 서비스를 더욱 안전하게 발전시켜 나가겠습니다.