GS리테일·티머니·알바몬: 비밀번호가 맞아도 막아야 했던 로그인

아프로·2026년 11월 1일

GS리테일·티머니·알바몬 크리덴셜 스터핑 표지

8개월. GS샵 회원 계정에 대량 로그인이 이어진 기간입니다. 그동안 회사는 이상을 알아차리지 못했습니다. 서버가 침해된 사고도 아니었습니다. 유출로 이어진 로그인은 비밀번호가 맞는 로그인이었는데, 개인정보보호위원회(이하 개인정보위)는 GS리테일에 과징금 128억 3,600만 원을 매겼습니다.

사고 원인: 로그인 이상탐지 부재

이번 편은 크리덴셜 스터핑(credential stuffing, 다른 곳에서 새어 나간 아이디·비밀번호 목록을 다른 서비스에 대량으로 넣어 보는 공격)입니다. 2024년 말부터 2025년까지 GS샵·GS25·티머니가 같은 방식으로 피해를 입었고, 알바몬도 같은 시기에 이력서 유출을 감지했습니다. 다만 알바몬은 공격 방식이 공식 발표되지 않아 이 글에서 크리덴셜 스터핑으로 단정하지 않습니다.

한눈에 보기

회사공격 기간피해제재
GS샵2024-06-21 ~ 2025-02-13 (약 8개월)1,581,025명GS리테일 합산 과징금 128억 3,600만 원 + 과태료 300만 원 (2026-08)
GS252024-12-26 ~ 2025-01-0479,128명(위와 합산)
티머니2025-0351,691명, 일부 마일리지 탈취과징금 5억 3,400만 원 (2026-01-28)
알바몬2025-04-30 감지이력서 22,473건(이름·휴대폰·이메일)처분 결과 미확인, 공격 방식 공식 미발표

GS샵·GS25·티머니 피해를 합하면 약 171만 명입니다. GS 두 곳에서 나간 항목은 이름·성별·생년월일·연락처·주소·이메일 여섯 가지입니다.

무슨 일이 있었나

  • 2024년 6월 21일 GS샵에 대량 로그인이 시작됩니다. 회사는 이를 탐지하지 못했습니다.
  • 2024년 12월 26일 ~ 2025년 1월 4일 GS25가 열흘 동안 공격을 받아 7만 9,128명의 정보가 빠져나갔습니다.
  • 2025년 2월 13일 GS샵 공격이 해를 넘겨서야 멈췄습니다.
  • 2025년 3월 티머니에서 5만 1,691명이 피해를 입었고, 일부는 마일리지까지 빼앗겼습니다.
  • 2025년 4월 30일 알바몬이 이력서 2만 2,473건의 유출을 감지했습니다.
  • 2026년 1월 28일 티머니, 8월 GS리테일 순서로 제재가 나왔습니다.

다른 곳의 유출 목록에서 자동 로그인 시도, 비밀번호만 확인하는 로그인 서버, 로그인만으로 열리는 회원정보 페이지를 거쳐 개인정보가 나가는 흐름과 막을 수 있었던 네 지점

출발점은 이 회사들이 아닙니다. 다른 사이트에서 이미 유출된 아이디·비밀번호 목록입니다. 같은 비밀번호를 여러 곳에 쓰는 사람이 많기 때문에, 목록을 자동으로 넣어 보면 대부분은 실패하고 일부는 맞습니다. 로그인 서버는 비밀번호만 확인했고, 맞으면 그대로 들여보냈습니다. 로그인에 성공하면 회원정보 페이지가 열렸고, 거기에 여섯 가지 개인정보가 있었습니다.

왜 뚫렸나

원인은 개인정보위가 GS리테일을 조사해 확인한 것입니다. 티머니는 공개된 원인 상세가 없어 여기서 다루지 않습니다.

  1. 대량 로그인을 탐지하지 못했습니다. 같은 IP에서 짧은 시간에 로그인이 쏟아져도 탐지도, 차단도 하지 못했습니다(개인정보위).
  2. 같은 IP가 두 곳을 공격했습니다. GS25 공격에 쓰인 IP 327개가 GS샵 공격에도 그대로 쓰였습니다. GS25 사고를 인지한 뒤에도 GS샵 유출은 계속됐습니다(개인정보위).
  3. 개인정보 전담 조직이 없었습니다(개인정보위).
  4. 통지가 늦었습니다. 추가로 확인된 피해자 1,599명에게 법정 기한 72시간을 넘겨 알렸습니다(개인정보위).

개인정보위의 판단은 분명합니다. 서버가 침해되지 않았더라도, 로그인 이상을 잡지 못하면 기업 책임이라는 것입니다.

개발자 시점: 내 서비스였다면

사고 회사의 코드가 아니라 같은 실수를 보여 주는 일반 예시입니다. 값은 모두 가짜입니다.

먼저 로그인입니다. 왼쪽 모양은 비밀번호만 확인합니다. 한 IP가 수많은 계정을 시도해도, 맞는 비밀번호가 나오면 세션을 내줍니다.

# 위험한 모양: 비밀번호만 확인
@app.post("/login")
def login(req):
    user = db.find_user(req.email)
    if not user:
        return fail()
    if not verify(req.pw, user.pw_hash):
        return fail()
    session["uid"] = user.id
    return ok()

# IP당 시도 횟수, 실패율: 보지 않음
# 처음 보는 기기: 알리지 않음

고친 모양은 IP와 계정별 시도 횟수를 먼저 셉니다. 한도를 넘으면 비밀번호를 보기도 전에 429(요청이 너무 많음)로 돌려보냅니다. 실패는 따로 집계해 실패율이 갑자기 오르면 알림을 보내고, 처음 보는 기기에서 로그인하면 본인에게 알립니다.

# 고친 모양
@app.post("/login")
def login(req):
    if limiter.over(req.ip, req.email):
        return too_many()          # 429
    user = check_password(req)
    if not user:
        fail_rate.add(req.ip)      # 알림 규칙
        return fail()
    if new_device(user, req):
        notify(user, "새 기기 로그인")
    session["uid"] = user.id
    return ok()

다음은 회원정보 페이지입니다. 로그인만으로 이름·주소·연락처 전체를 그대로 내보내면, 로그인 한 번이 곧 개인정보 전체가 됩니다.

# 위험한 모양: 로그인만으로 전체 공개
@app.get("/me/profile")
@login_required
def profile():
    u = db.profile(session["uid"])
    return u
# 이름·주소·연락처 전체가 그대로

고친 모양은 최근에 다시 인증한 경우에만 보여 주고, 연락처는 가려서 내보냅니다. 알림 규칙의 숫자는 예시 값이므로 서비스 트래픽에 맞춰 정합니다.

# 고친 모양: 재인증 + 마스킹 + 알림 규칙
@app.get("/me/profile")
@login_required
@recent_auth(max_age_min=5)
def profile():
    u = db.profile(session["uid"])
    return mask(u)   # 010-****-0000

# 알림 규칙 (예시 값)
ALERTS = [
  ("login_fail_rate", 0.3, "5m"),
  ("accounts_per_ip", 20, "10m"),
]

로그인 기록은 이미 서버에 쌓이고 있습니다. 위 코드에서 새로 생긴 것은 그 기록을 세는 규칙과, 기준을 넘었을 때 누가 알림을 받는지 정한 것뿐입니다.

막을 수 있었던 네 지점

  1. 실패율·IP당 시도 규칙. 로그인 실패율과 IP당 시도 횟수를 보는 단순한 규칙만 있었어도 8개월 동안 모르고 지나갈 수는 없었습니다.
  2. 그룹사 공격 IP 공유. GS25와 GS샵에는 같은 IP 327개가 쓰였습니다. 그룹사끼리 공격 IP를 공유했다면 GS샵 피해는 크게 줄었습니다.
  3. 다중인증(MFA)과 새 기기 알림. 로그인에 인증 단계를 하나 더 두면 유출 목록만으로는 들어올 수 없습니다. 새 기기 알림은 계정 주인이 먼저 이상을 알게 해 줍니다.
  4. 회원정보 재인증·마스킹. 회원정보 수정 페이지에 재인증을 요구했다면, 로그인만으로 개인정보를 모두 볼 수는 없었습니다.

같은 사고를 막는 점검 5줄

  • IP·기기·계정별 로그인 속도 제한이 있다. 봇 탐지도 검토한다.
  • 로그인 실패율이 급증하면 알림이 가고, 받는 사람이 정해져 있다.
  • 다중인증과 새 기기 로그인 알림을 켰다. 패스키 도입을 검토한다.
  • 가입·비밀번호 변경 때 알려진 유출 비밀번호를 거부한다.
  • 회원정보처럼 민감한 페이지는 재인증을 요구하고, 개인정보는 가려서 보여 준다.

제재와 비용

회사처분시점
GS리테일(GS샵·GS25 합산)과징금 128억 3,600만 원 + 과태료 300만 원2026년 8월
티머니과징금 5억 3,400만 원2026년 1월 28일
알바몬처분 결과 미확인2026년 10월 말 기준

모두 개인정보위 처분 기준입니다. 알바몬은 2026년 10월 말 기준으로 처분 결과도, 공격 방식도 공식 발표를 확인하지 못했습니다. 이 글은 법률 자문이 아닙니다.

정리

  • 크리덴셜 스터핑은 서버를 침해하지 않고, 다른 곳에서 새어 나간 비밀번호로 정상 로그인을 합니다.
  • 비밀번호가 맞는지가 아니라, 그 로그인이 평소와 같은지를 봐야 합니다. 개인정보위는 이를 보지 못한 책임을 과징금으로 물었습니다.
  • 속도 제한, 실패율 알림, 다중인증·새 기기 알림, 민감 페이지 재인증이 네 겹의 방어선입니다.

더 깊게

보안을 더 알고 싶다면

오늘 본 사고의 원인은 대부분 서버와 코드의 기본기에서 막을 수 있는 것들이었습니다. 직접 손으로 막아 보고 싶다면 이런 강의도 있습니다.

백엔드 개발자를 위한 서버 보안 실무: 해킹 방어부터 정보유출 방지까지

  • 서버를 공격하는 흐름을 로그로 보고, 내 서버와 Spring·Express 코드에서 직접 막아 봅니다.
  • SSH 하드닝, 인젝션, 인증·인가, 시크릿과 개인정보 보호, 침해사고 첫 한 시간까지 8개 섹션 54개 수업입니다.
  • 이 글과 이어지는 수업: 3-5 무차별 대입과 크리덴셜 스터핑 막기, 3-6 MFA와 세션 탈취 대응: 피싱에 강한 인증과 토큰 바인딩, 4-4 Rate limiting과 멱등성 키 설계

출처와 확인 날짜

수치는 2026년 10월 말 기준 공개 보도·발표입니다. 진행 중인 사건은 바뀔 수 있고, 이 글은 법률 자문이 아닙니다.


이전 글 랜섬웨어 2연타, 예스24·SGI서울보증: 백업이 같이 잠기면
다음 글 AI가 바꾼 공격: 2026 금융권 연쇄 해킹과 AI 활용 해커
시리즈 전체 목록 해킹 사고 해부 전체 목록

profile
고인물 개발자(OG)

0개의 댓글