다른 회사 데이터가 보였다 — 14개 테이블에 보안이 없었던 이야기

문제 해결 노트··13분 읽기·
14개 테이블에 RLS를 적용하는 멀티테넌트 보안 강화 과정

등골이 서늘해진 순간

Echo Mail을 테스트하고 있었어요. A 계정으로 로그인했는데, 화면에 B 계정의 업체 목록과 알림 이력이 뜨는 거예요.

다시 말하면 — 다른 회사의 데이터가 내 화면에 보인 겁니다.

Echo Mail은 여러 회사가 같이 쓰는 서비스(SaaS)예요. 각 회사의 데이터는 철저하게 분리되어야 하는데, 그 분리가 안 되고 있었습니다. 비유하면 아파트인데 현관문이 전부 열려있는 상태.

원인을 찾아보니 — 14개 테이블에 잠금장치가 없었다

데이터베이스에는 "이 사용자는 이 데이터만 볼 수 있다"는 잠금장치(Row Level Security, 줄여서 RLS)를 걸 수 있어요. 아파트로 치면 각 집의 현관문 잠금장치인데, 14개 방(테이블) 전부에 잠금장치가 없었습니다.

그동안은 앱 코드에서 "A 회사 사용자에게는 A 회사 데이터만 보여줘"라고 필터링하고 있었어요. 근데 이 필터가 실수로 빠지는 순간, 전체 데이터가 그대로 노출되는 구조였습니다.

실제로 필터에 넘기는 회사 ID 값이 비어있을 때(undefined) 필터가 무시되면서 모든 데이터가 나오는 게 원인이었어요.

AI한테 보안 감사를 시키다

"보안 강화해줘"로는 일반적인 체크리스트만 나왔어요. 실제 증상을 알려줘야 했습니다:

당신은 데이터베이스 보안 전문가입니다.

[현재 상태]
- 여러 회사가 같이 쓰는 SaaS
- 앱 코드에서 회사별 데이터 필터링 중
- 데이터베이스에 잠금장치(RLS)가 하나도 없음
- 실제 증상: 필터 값이 비어있으면 다른 회사 데이터가 노출됨

[요청]
1. 보안이 필요한 테이블 14개 전부 식별해주세요
2. 각 테이블에 "내 회사 데이터만 보이는" 잠금장치를 설계해주세요
3. 신규 가입 시 자동으로 회사가 생성되는 장치도 만들어주세요
비교 "보안 강화해줘" 바꾼 프롬프트
역할 부여 없음 "데이터베이스 보안 전문가"
증상 설명 없음 다른 회사 데이터 노출 + 원인
범위 불명확 14개 테이블 전부
결과 일반 체크리스트 432줄 보안 분석 리포트
AI가 14개 테이블 각각에 대해 "누가 볼 수 있는지" 잠금 정책을 설계해줬어요.

고치면 다음 문제가 터지는 4연쇄

여기서부터 삽질이 시작됐습니다. 한 문제를 고치면 다음 문제가 터지는 연쇄가 4번이나 반복됐어요.

1단계: 잠금장치 설치 → 신규 가입 불가

14개 테이블에 잠금장치를 달았더니, 새로운 사용자가 가입하면 아무것도 안 보이는 문제가 생겼어요. 아파트에 현관문 잠금을 설치했는데, 새 입주자가 열쇠를 못 받은 셈.

가입할 때 자동으로 "회사 + 열쇠"를 만들어주는 장치가 필요했습니다:

잠금장치 설치 후 신규 가입자에게 회사가 생성되지 않습니다.
가입 시 자동으로 회사와 소속 정보를 만들어주는
데이터베이스 트리거를 만들어주세요.
2단계: 트리거 추가 → 기존 코드에서 데이터 안 나옴

트리거로 해결했더니, 이번에는 기존 코드에서 데이터가 하나도 안 나왔습니다. 잠금장치가 "누구세요?"라고 물어보는데, 기존 코드는 자기 신분증을 제시하는 방법을 몰랐던 거예요.

문제는 데이터베이스 접속 방식의 차이였습니다. 웹에서 접속하면 자동으로 신분증이 전달되는데, 기존 코드는 직접 연결이라 신분증이 빠져있었어요.

기존 코드에서 데이터가 안 나옵니다.
직접 연결 방식에서는 사용자 신분 정보가 전달되지 않습니다.
데이터베이스 쿼리 전에 "나는 이 사용자입니다"라는 정보를
설정하는 코드를 추가해주세요.
3단계: 신분증 설정 → 백그라운드 작업 멈춤

해결했더니, 이번에는 5분마다 돌아가는 메일 감시가 멈췄습니다. 백그라운드 작업은 특정 사용자가 아니라 "시스템"으로 돌아가는 건데, 잠금장치가 "시스템이라도 신분증 없이는 안 돼"라고 막은 거예요.

백그라운드 작업(메일 감시, 스케줄러)이 멈췄습니다.
시스템 관리자 역할에는 잠금장치를 우회하는
별도 정책을 추가해주세요.
4단계: 최종 해결

시스템 관리자 역할에 대한 우회 정책을 추가해서 드디어 전부 동작.

단계 뭘 고쳤나 뭐가 터졌나
1 14개 테이블에 잠금장치 신규 가입 불가
2 가입 시 자동 생성 트리거 기존 코드에서 데이터 안 나옴
3 쿼리 전 신분 정보 설정 백그라운드 작업 멈춤
4 시스템 관리자 우회 정책 드디어 해결
처음부터 4단계를 계획한 게 아니에요. 1단계 고치면 2단계가 터지고, 2단계 고치면 3단계가 터지는 식이었습니다. 보안 관련 작업이 이렇게 연쇄적인 줄 몰랐어요.

검증 — "진짜 안 보이나?"

고쳐놓고 "진짜 된 건가?" 확인이 필요했습니다. AI한테 테스트 시나리오를 만들게 했어요:

  • A 회사 사용자가 B 회사 데이터를 조회 → 빈 결과 (이전에는 B 데이터가 보였음)
  • 회사 ID가 비어있을 때 → 빈 결과 (이전에는 전체 데이터가 나왔음)
  • 신규 가입 → 자동으로 회사 생성됨
  • 메일 감시 스케줄러 → 정상 동작
13개 테스트 전부 통과. 보안 위험도가 "심각"에서 "낮음"으로 변경됐습니다.

배운 것 — 앱 코드만 믿으면 안 된다

이 삽질에서 가장 크게 배운 건:

앱에서 아무리 잘 필터링해도, 데이터베이스 자체에 잠금이 없으면 언젠가 새요. 앱 코드에 버그가 하나만 있어도 전체 데이터가 노출되는 구조는 위험합니다. 데이터베이스 잠금장치(RLS)는 "앱이 실수해도 데이터는 안전한" 마지막 방어선이에요.

그리고 보안 작업은 연쇄 반응이 심합니다. 잠금 하나 걸면 다른 데서 터지고, 그거 고치면 또 다른 데서 터지고. "하나만 고치면 끝"이 아니라 "하나 고치면 3개 더 나온다"는 마음으로 시작해야 해요.

AI한테 시킬 때는 "보안 강화해줘"가 아니라, 실제로 어떤 증상이 있는지, 몇 개 테이블이 관련되는지, 어떤 역할(사용자/관리자/시스템)이 접근하는지를 알려줘야 쓸 수 있는 결과가 나옵니다.

공유

댓글