3P by GN⁺ | ★ favorite | 댓글 1개
  • 잘못 설정된 Firebase 보안 규칙 하나가 수백 개 사이트의 사용자 데이터 노출로 이어졌고, 확인된 규모만 약 1억 2,460만 개 레코드에 달함
  • 조사는 웹사이트와 로드된 JavaScript 번들에서 Firebase 설정 변수를 찾는 방식으로 시작됐으며, Python 스캐너의 메모리 문제 때문에 Go로 재작성됨
  • 보조 스캐너 Catalyst는 Firebase 컬렉션의 읽기 가능 여부를 자동 확인하고, 샘플 데이터를 바탕으로 노출된 정보 유형과 규모를 추정함
  • 집계된 노출 항목에는 이름 84,221,169개, 이메일 106,266,766개, 전화번호 33,559,863개, 평문 비밀번호 20,185,831개, 결제 정보 27,487,924개가 포함됨
  • 연구진은 13일 동안 842건의 신고 이메일을 보냈지만, 설정을 고친 사이트 소유자는 24%, 회신한 비율은 1%, 버그 바운티를 제안한 곳은 2개 사이트에 그침

인터넷 규모 Firebase 노출 스캔

  • Chattr.ai 침해 사례 이후, 잘못 설정된 Firebase 인스턴스를 통해 노출된 PII를 찾기 위한 인터넷 전체 스캔이 시작됨
  • 첫 Python 스캐너는 웹사이트나 로드된 .js 번들에서 Firebase 설정 변수를 확인했지만, 약 500개 스레드로 동작하면서 메모리 사용량이 늘었고 1시간 안에 OOM이 발생함
  • 이후 Go로 재작성된 스캐너는 550만 개 도메인을 대상으로 실행됐고, 처음 예상한 약 11일보다 긴 2~3주가 걸림
  • 초기 수작업 검토만으로도 136개 사이트와 620만 레코드를 찾았지만, 후보 목록이 방대해지면서 완전 자동화가 필요해짐

Catalyst와 노출 규모 산정

  • Catalyst는 후보 사이트나 JavaScript 번들을 입력으로 받아, 공통 Firebase 컬렉션과 JavaScript에 직접 언급된 컬렉션의 읽기 접근 가능 여부를 자동 확인함
  • 읽기 가능한 컬렉션을 찾으면 100개 레코드 샘플을 수집해 포함된 정보 유형을 확인하고, 이를 컬렉션 전체 크기에 곱해 영향을 추정함
  • 결과 저장소로는 PostgreSQL 기반 오픈소스 Firebase 경쟁 제품인 Supabase가 선택됐고, 정리된 데이터는 비공개 데이터베이스 테이블에 업로드됨
  • 집계 수치는 다음과 같으며, 실제 노출 규모는 표시된 수치보다 클 가능성이 남아 있음
    • 전체 레코드: 124,605,664개
    • 이름: 84,221,169개
    • 이메일: 106,266,766개
    • 전화번호: 33,559,863개
    • 비밀번호: 20,185,831개
    • 결제 정보: 은행 정보, 인보이스 등 27,487,924개

영향이 컸던 사이트

  • Silid LMS

    • 학생과 교사를 위한 학습 관리 시스템
    • 이름, 이메일, 전화번호를 포함해 2,700만 명의 사용자 레코드가 노출돼 가장 큰 규모를 기록함
  • 온라인 도박 네트워크

    • 서로 리스킨된 9개 사이트로 구성됨
    • 일부 스핀은 승리 확률이 0%가 되도록 조작돼 있음
    • 은행 계정 상세 로그인 정보가 800만 개로 가장 많이 노출됨
    • 평문 비밀번호도 1,000만 개로 영향 사이트 중 최대 규모였음
    • 문제를 신고하려는 과정에서 고객 지원이 대화 중 추파를 던짐
  • Lead Carrot

    • 콜드콜용 온라인 리드 생성 서비스
    • 노출된 사용자 정보 규모가 상위 3위이며 2,200만 명에게 영향이 있음
  • MyChefTool

    • 레스토랑용 비즈니스 관리 앱 및 POS 애플리케이션
    • 노출된 이름 수 1위, 이메일 수 2위로 각각 1,400만 개1,300만 개가 노출됨

신고 이후의 반응

  • 연구진은 13일 동안 842건의 이메일을 발송함
    • 85%는 전달됐고 9%는 반송됨
    • 사이트 소유자의 24%가 잘못된 설정을 수정함
    • 회신한 사이트 소유자는 1%에 그침
    • 0.2%에 해당하는 2개 사이트 소유자가 버그 바운티를 제안함

댓글과 토론

Hacker News 의견들
  • Firebase에서 오래 일했는데, 보안 규칙 문제는 제품을 계속 괴롭혀 왔음
    자동 만료되는 기본 규칙, 교육 강화 등 여러 접근을 해봤지만 결국 여전히 안전하지 않은 데이터베이스가 많이 보임
    이유는 복잡한데, Firebase식 보안 규칙은 아직 낯선 개념이고, 기존 위치에 데이터를 추가하는 새 개발자가 데이터의 개인정보 요구사항 변화에 맞춰 규칙까지 고치지 않는 경우가 많음
    또 각자 만든 백엔드가 주던 “모호함에 의한 보안”이 사라져 대규모 스캔이 쉬워짐
    특히 Realtime Database의 규칙은 쓰기 어렵고 확장성도 좋지 않지만, 자동 스캔은 보통 열린 데이터만 찾으므로 read write true보다 조금만 나아도 막을 수 있었음
    기술적으로 Firebase 접근 자체가 틀린 건 아니지만, 저장된 데이터와 보안 규칙 중심 모델을 쓰는 거의 유일한 백엔드라 오해와 오용, 이런 사고에 노출되기 쉬움

    • 솔직히 프론트엔드가 데이터베이스에 직접 데이터를 쓸 수 있는 모델은 보안 규칙이 있어도 늘 의심스러웠음
      백엔드에서는 검증·보안 규칙이 명세의 일부로 보이지만, Firebase 보안 규칙은 별도 절차라 쉽게 잊히고 새 기능을 만들 때마다 다시 평가해야 함
    • Google 지원 채널로 연락해서 도와주거나 웹사이트들에 문제를 알릴 수 있게 해달라고 했지만, 원하면 대신 기능 요청을 만들어 주겠다는 답만 받았음
      프로젝트 소유자에게 알릴 수 있는 사람의 주의를 끌려면 Firebase 내부에서 꽤 높은 단계까지 에스컬레이션해야 했을 것 같음
    • https://firebase.google.com/docs/rules/basics를 보면, 미리 정해진 보안 규칙 템플릿만 고르는 간단 보안 모드가 실용적일지 궁금함
      예를 들어 글의 “콘텐츠 소유자만 접근”이나 “속성 기반·역할 기반 접근” 같은 템플릿이 대부분 앱의 패턴을 덮을 수 있는지, 아니면 정말 맞춤 규칙이 많이 필요한지 의문임
      보안 규칙 작성의 큰 문제는 거의 모든 실수가 보안 문제가 되므로 필요 없으면 건드리고 싶지 않다는 점임
      규칙이 너무 잠겨 있으면 앱이 안 돌아가서 바로 티가 나지만, 너무 열려 있으면 과도한 접근을 직접 찔러보기 전엔 잘 드러나지 않음
      관련해서 각 보안 규칙마다 접근 거부 예시 테스트 케이스를 개발자가 작성하도록 강제하는 방식도 생각해볼 만함
    • 우리에게는 간단한 요령 하나가 크게 도움 됐음: fireplan이라는 규칙 트랜스파일러가 모든 속성에 기본 "$other": {".read": false, ".write": false} 규칙을 추가함
      덕분에 새 필드는 명시적으로 추가해야 해서, 새 값이 기존 규칙을 자신도 모르게 “상속”하는 일이 거의 불가능해짐
      Firebase를 10년 넘게 써와서 최신 규칙 도구도 이렇게 하는지는 모르겠음
      정말 도움이 될 부분은, 통제하기 어려운 여러 클라이언트 버전이 있는 상황에서 필드 이름 변경이나 데이터 구조 변경을 기본 지원하는 것, 데이터베이스를 띄우지 않고도 규칙을 가볍게 테스트하는 방법, 운영 환경에서 규칙 실패 시 더 나은 디버깅 정보를 주는 것임
      실패할 때마다 규칙이 접근한 모든 값을 함께 기록해야, 변하는 데이터 때문에 생기는 일시적 실패를 디버깅할 수 있음
    • Firebase와 Firestore를 꽤 옹호해왔지만 위 내용에는 모두 동의함
      이건 충분히 설명되지 않은 개념 모델
      프로젝트에서는 각 컬렉션에 공개, 사용자 데이터, 인증 사용자만 공개, 관리자 전용 같은 보안 프로필이 있어야 한다고 말하고, 컬렉션마다 맞춤 조건을 쓰기보다 보안 규칙 함수로 이런 범주를 강제하는 식으로 접근함
      필드 단위가 아니라 컬렉션 단위로 보안을 생각하면 한 문서 안에 서로 다른 보안 의도가 섞이는 일을 줄일 수 있음
      컬렉션이 공개라면 공개가 아닌 필드를 담으면 안 되고, 필요하면 Firestore 트리거로 민감한 컨텍스트에서 공개 컨텍스트로 데이터를 복제할 수 있지만 반대 방향은 안 됨
      문제는 규칙 자체 바깥에 규칙의 의도를 문서화해야 해서 잘못 적용하기 쉽다는 점이고, 예전에는 테스트 작성도 고통스러웠지만 지금은 많이 나아짐
  • “How I pwned half of America’s fast food chains, simultaneously.”가 떠오름: https://mrbruh.com/chattr/
    HN: https://news.ycombinator.com/item?id=38933999

    • 두 글 모두 내가 썼고, 이번 글은 그 블로그 글의 후속편
    • 정상임. 블로그 글의 여섯 번째 단어가 그 글로 연결되는 링크임
      After the initial buzz of [pwning Chattr.ai] had settled down, […]
  • 내가 잘못 본 게 아니라면, 글 끝 기준으로 이런 취약점이 있는 사이트의 75% 가 아직 그대로 열려 있고 덤프 가능한 상태라는 뜻 아닌가?
    미친 일임
    어떤 날은 컴퓨터를 만지려면 면허가 있어야 한다는 생각도 듦

    • 많은 회사에는 상근 개발자가 없음
      웹사이트를 만들어주는 대행사에 외주를 주고, 대행사는 개발자를 계속 교체하며 처음엔 좋은 개발자를 붙였다가 이후에는 회사가 불평하지 않는 한 덜 숙련된 개발자에게 계약을 맡기는 식임
      취약점 이메일은 스팸으로 무시됐거나 전달 후 방치됐거나, PM의 회의 일정 대기열에 들어가 클라이언트에게 최대한 청구하면서 고치려는 항목이 됐을 가능성이 큼
      전문 면허가 의무인 분야에도 무능한 면허 소지자는 많음
      의사도 교육과 면허 요건이 엄청나지만 돌팔이 의사와 면허 있는 대체의학 종사자는 부족하지 않음
    • 슬프지만 사실이고, 아마 훨씬 더 많을 것임
      각 사이트에 영향을 받은 내용, 고치는 방법, 연락 방법을 담아 맞춤 이메일을 보내며 최선을 다했음
    • 맞음. 그래서 악의적 행위자가 즉시 악용하지 못하게 영향받은 사이트 목록을 공개할 수 없었음
    • 개인정보 유출로 회사가 망하지 않는 한, 그들에게는 사업 비용이고 그 비용은 사용자에게 전가됨
  • 이건 PM의 cheap-fast-good 삼각형에서 싸고 빠른 선택을 한 필연적 결과임
    안타깝게도 일부 고객과 사용자의 우려는 논의에서 빠졌고, 그들의 개인정보가 비용이 됐음
    여기 나열된 회사 중 그런 결정을 하고도 리더십이 바뀌지 않은 곳은 조심할 것 같음
    많은 회사가 고객을 보호할 만큼 충분히 신경 쓰지 않는다는 건 여러 번 증명됐고, 역사는 반복됨

    • 대체로 동의하지만, 아주 적긴 해도 감사해하며 빠르게 고친 좋은 사례도 있었음
  • Firebase에 대해 아주 기본적인 질문이 있음: 이 글의 앱 대부분이 커스텀 서버 코드 없이 정적 호스팅된 클라이언트 측 JavaScript만으로 구현된 건가?
    백엔드는 100% Google이 호스팅하는 Firebase 설정이라는 뜻인지 궁금함
    그렇다면 수백만 사용자가 있는 사이트에서 그런 아키텍처가 이렇게 흔해졌다는 걸 몰랐음

    • 맞음. 완전히 클라이언트 측이거나, 서버를 거치더라도 순진하게 통과시키는 방식임
      API에서 기본 허용 보안 모델을 두면 필연적으로 이렇게 됨
      안타깝게도 JavaScript 개발자를 겨냥한 라이브러리에는 안전하지 않은 기본값이 흔하고, GraphQL도 이런 문제가 나올 만한 영역으로 보임
    • 섞여 있을 수도 있음
      Firebase에는 클라우드에서 호출 가능한 함수인 Firebase Functions도 있고, 그 코드는 공개되지 않음
      하지만 Firestore나 Firebase Realtime Database는 모두 사용자가 보안 규칙을 설정해야 하며, 그렇지 않으면 누구나 모든 데이터를 읽을 수 있음
    • 꽤 과격한 구성처럼 보이지만, 백엔드의 SQL 스키마에 적절한 인가 규칙을 코딩하면 동작할 수 있음
      백엔드에서 적절한 인가 규칙을 쉽게 작성할 수 있게 만드는 게 중요함
  • 이런 걸 보면 오래전에 비밀번호 관리자와 가상 카드를 선택해둔 게 다행스럽게 느껴짐
    그래도 인터넷은 더 무서워짐
    대부분은 웹이 얼마나 취약하고 자신들이 얼마나 노출돼 있는지 전혀 모름

    • 앞으로 더 나빠질 것 같음
      AI 에이전트가 봇보다 훨씬 효율적으로 취약점을 찾게 될 테니, 이상한 미래가 기다리는 느낌임
    • 시간이 갈수록 서비스들이 웹사이트 만들기를 쉽게 만들고 더 많은 것을 추상화해서, 개발자들이 뭘 설정해야 하는지 모르게 됨
    • 비밀번호 관리자만으로는 부족함
      가입하는 서비스마다 고유 이메일 주소를 써야 함
      사고가 났을 때 피해를 제한하고, 다른 서비스와 대조해 공개 정보 수집을 당하는 것도 막을 수 있음
      가끔 그 고유 주소로 악성 이메일이 오면 사이트 운영자보다 먼저 침해를 알아차릴 수도 있음
  • “스레드가 약 500개인 Python 프로그램은 시간이 지나며 메모리를 먹기 시작한다”는 부분에 대해 더 아는 사람이 있나?
    나도 Python으로 스레드 수백 개짜리 스크레이퍼를 갖고 있는데 메모리를 많이 먹는 것 같음
    우회책이 있는지, 아니면 다른 언어로 다시 쓰는 게 유일한 해결책인지 궁금함

    • Python으로도 할 수는 있지만, Python의 참조 카운팅이 스레드와 어떻게 상호작용하는지 파고들어야 함
      개인적으로는 스레드보다 프로세스를 선호하고, 공유 메모리보다는 작업자 풀과 메시지 버스를 씀
      이 해법도 단점과 약간의 오버헤드는 있지만 메모리 문제를 훨씬 덜 걱정해도 됨
      크롤러는 프로세스 수가 비교적 일정하고 각 프로세스의 작업이 독립적이라 프로세스 모델이 더 잘 맞아 보임
    • import multiprocessing as threading
    • 정확한 문제를 모르지만, 솔직히 Python은 이런 작업에 맞는 언어가 아님
      다시 쓰는 게 실질적인 유일한 해결책에 가까움
    • 이 용도에는 asyncio를 쓰는 게 맞음
      아주 잘 맞는 사용 사례임
  • 잘했음
    영향받은 사용자 수가 실제로 더 많을 가능성이 높다는 결론을 어떻게 냈는지 궁금함
    보기에는 도박 사이트나 Lead Carrot 같은 일부 사이트에는 가짜 계정 데이터가 많이 섞였을 수도 있어 보임

    • 여러 사이트를 수동 검토해보니, 자동 스캐너가 변수명으로 전화번호 같은 알려진 데이터 유형을 확인하는 방식이라 영어가 아닌 개인정보를 잘 식별하지 못했음
      영어 사이트에서만 잘 동작하는 방식임
    • 도박 사이트 데이터가 가짜가 아니라는 건 확인했지만, Lead 쪽은 모르겠음
      더 많다고 한 이유는 스캔 목록에 없던 다른 서비스들도 취약할 가능성이 있기 때문임