당신의 클라우드 데이터베이스는 보안 연극이다
Source: Dev.to
전통적인 멀티‑테넌트 SaaS 스토리지의 문제
멀티‑테넌트 SaaS 스토리지는 근본적으로 아키텍처 수준에서 취약합니다. 공급업체는 행 수준 보안, 모든 쿼리에서 테넌트 ID, 그리고 휴면 시 암호화를 약속하지만, 단 하나의 SQL 인젝션이나 권한 상승 취약점으로 전체 테넌트의 데이터를 노출시킬 수 있습니다. 한 번의 침해로 수천 명의 피해자가 발생할 수 있습니다.
전통적인 SaaS 아키텍처
┌─────────────────────────────────┐
│ Application Layer │
│ (trusted, handles isolation) │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Database (ALL tenant data) │
│ WHERE tenant_id = ? │
└─────────────────────────────────┘
WHERE절이 하나라도 누락되거나, API 엔드포인트가 손상되거나, IDOR 결함이 있으면 모든 테넌트가 위험에 처합니다.- “휴면 시 암호화”는 암호화 키가 쿼리를 처리하는 시스템과 동일한 곳에 존재하기 때문에 효과가 없습니다. 이는 물리적 도난에 대해서만 보호하고, 데이터베이스에 접근할 수 있는 공격자에게는 방어가 되지 않습니다.
Source: …
BSFS: 스토리지 계층에서의 암호화 격리
BSFS (Block Storage File System)는 애플리케이션 내부의 공유 데이터베이스와 신뢰 경계를 없앱니다. 키가 없으면 데이터는 존재하지 않는 것입니다.
핵심 개념
- 공유 데이터베이스 없음 – 각 테넌트는 자체 암호화 파티션을 사용합니다.
- 암호화 격리 – 메타데이터와 데이터는 테넌트별 키로 암호화되며, 별도의 접근 제어 검사가 필요하지 않습니다.
암호화된 블록 할당 테이블(BAT)을 통한 원자 커밋
- 무작위 블록 주소를 할당합니다.
- 해당 블록에 새 파일 데이터를 씁니다.
- 암호화된 BAT를 업데이트합니다.
- 이전 블록을 자유롭게 표시합니다.
BAT 업데이트가 커밋 지점입니다. 프로세스가 쓰기 중에 충돌하면, 읽는 쪽은 여전히 이전 파일을 보게 되고, 새 블록은 쓰레기가 됩니다. 손상도, 부분 쓰기도, “점진적 일관성” 같은 말도 없습니다.
// The atomic commit point
bsfs_encrypt_bat(partition_key, bat, encrypted_bat);
fseek(blob_file, partition_offset, SEEK_SET);
fwrite(encrypted_bat, sizeof(bsfs_bat_t), 1, blob_file);
fflush(blob_file);
// Transaction committed. Readers see new file.
fwrite이전에 충돌 → 이전 파일은 그대로 유지됩니다.fwrite이후에 충돌 → 새 파일이 커밋됩니다.
키 파생
각 테넌트는 마스터 키를 받습니다. 파티션 키는 HKDF를 통해 파생됩니다:
Master Key → HKDF(partition_id) → Partition Key
- 마스터 키는 어떤 파티션 키도 파생할 수 있습니다.
- 파티션 키는 형제 키를 파생할 수 없습니다.
이렇게 하면 단순한 접근 제어 검사가 아니라 진정한 암호화 격리를 제공하게 됩니다.
무작위 블록 할당
BSFS는 Fisher‑Yates 셔플을 사용해 블록 배치를 의도적으로 무작위화합니다. 파일이 파티션 전체에 흩어져 저장되므로, 공격자가 블록 접근 패턴만으로 파일 구조나 관계를 추론하기 어렵습니다.
BSFS가 무엇이며, 무엇이 아닌가
대체 대상이 아님
- PostgreSQL 또는 기타 관계형 데이터베이스
- 분산 파일 시스템
- 키‑값 저장소
- 수백만 개의 작은 파일 또는 범위 쿼리에 최적화된 시스템
의도된 사용 사례
- 진정한 테넌트 격리
- 원자적 파일 업데이트
- 암호화된 메타데이터
- SaaS 애플리케이션 스토리지에 대한 예측 가능한 성능
암호화된 객체 스토리지(예: 암호화 경계가 적용된 S3 버킷)로 생각하면, tenant_id 열이 있는 행과는 다릅니다.
API 예제
C 예제
uint8_t master_key[32];
bsfs_tenant_t tenant;
bsfs_tenant_init(&tenant, "storage.blob", master_key);
bsfs_write_file(&tenant, file_id, data, size);
bsfs_read_file(&tenant, file_id, &data, &size);
bsfs_delete_file(&tenant, file_id);
bsfs_tenant_cleanup(&tenant);
Python 예제
from bsfs import BSFS, generate_master_key
import uuid
master_key = generate_master_key()
with BSFS('storage.blob', master_key) as fs:
file_id = uuid.uuid4()
fs.write_file(file_id, b'confidential data')
data = fs.read_file(file_id)
fs.delete_file(file_id)
운영 제한
- 파티션당 64개의 파일
- 최대 파일 크기 512 MiB (2 MiB 블록 × 256 블록)
- 단일 파티션 구현 (다중 파티션 계획 중)
- 스트리밍 I/O 없음 – 전체 파일이 메모리로 로드됨
- 단일 스레드
BSFS를 사용해야 할 때
- 다중 테넌트 SaaS 제품 구축
- 규제 준수에 따라 엄격한 데이터 격리 필요
- 행 수준 보안 버그 경험이 있음
- 파일, 문서 또는 블롭에 대한 검증 가능한 암호학적 경계 필요
BSFS를 사용하지 말아야 할 경우
- 관계형 쿼리 또는 복잡한 조인이 필요할 때
- 수백만 개의 작은 레코드를 저장할 때
- 분산 합의 또는 고가용성 복제가 필요할 때
- 위협 모델에 데이터베이스 침해가 포함되지 않을 때
비용 비교
클라우드 데이터베이스는 IOPS, 스토리지, 컴퓨팅 비용을 청구합니다. BSFS는 로컬 NVMe에서 실행되며, 원시 디스크 I/O를 사용하고 메타데이터만 암호화합니다. 이를 통해 다음을 피할 수 있습니다:
- 데이터베이스 연결 오버헤드
- 쿼리 옵티마이저 실행 시간
- 복제 지연
- “서버리스” 마크업 비용
- 테넌트당 데이터베이스 인스턴스
단일 2 TB NVMe 드라이브는 진정한 암호화 격리를 제공하면서도 한 달에 단일 관리형 RDS 인스턴스 비용보다 적은 비용으로 수십 개의 테넌트를 지원할 수 있습니다.
Conclusion
SaaS에서 대부분의 보안은 접근 제어 목록—사회적 관습에 의해 시행됩니다. BSFS는 다중 테넌트 데이터 접근을 암호학적으로 불가능하게 만들며, 단순히 허가되지 않은 것이 아닙니다. 이는 수십 년간 손상된 지름길을 거친 후 원칙으로 되돌아갑니다.
추가 자료
- 비디오 워크스루 (구현, 복사‑온‑쓰기 세만틱스, 암호화된 메타데이터)
- 소스 코드 저장소: