Zero-Trust Personal Information Manager와 Client-Side Encryption 설계
Source: Dev.to
저는 Amrita Vishwa Vidyapeetham에서 B.Tech 컴퓨터 과학 학부생이며, privacy‑focused 시스템을 구축하고 실제 소프트웨어를 엔드‑투‑엔드로 배포하면서 배우는 것을 즐깁니다.
동기
InfoStuffs의 시작은 단순히 또 다른 생산성 애플리케이션을 만들고자 하는 욕구에서 비롯된 것이 아닙니다. 제 여동생의 매우 구체적인 사용자 요구에서 출발했습니다. 그녀는 개인 문서와 민감한 메모를 정리할 디지털 공간이 필요했지만, Google Keep이나 Notion 같은 일반적인 클라우드 서비스를 사용하기를 거부했습니다.
그녀의 제약 조건은 간단했지만 기술적으로는 까다로웠습니다: 평문 데이터를 클라우드 제공자에게 맡기지 않으면서도 클라우드의 편리함을 원한다는 것이었습니다.
이 도전 과제가 InfoStuffs의 기반이 되었습니다. 저는 단순한 웹 애플리케이션을 구축하는 목표에서 Zero‑Trust Information Management System을 설계하는 목표로 전환했으며, 이는 기본적으로 프라이버시를 최우선으로 합니다.
문제 설명
현대 생산성 도구는 일반적으로 두 가지 범주로 나뉩니다:
| 카테고리 | 예시 | 단점 |
|---|---|---|
| 편리한 SaaS | Notion, Google Keep | 사용자 데이터를 평문으로 저장하거나 서버 관리 키를 사용하여 내부 유출이나 데이터베이스 침해에 취약합니다. |
| 셀프‑호스팅 | Obsidian, Nextcloud | 강력한 프라이버시를 제공하지만 여러 기기에서 접근하고 유지 관리하기가 어렵습니다. |
InfoStuffs는 이 격차를 메워줍니다. 시스템은 서버가 완전히 침해되더라도 쓰레기 데이터만 얻을 수 있을 정도로 충분히 안전하면서도, 모든 기기에서 표준 웹 브라우저를 통해 접근할 수 있어야 했습니다.
고수준 아키텍처
이러한 제약을 만족시키기 위해 InfoStuffs는 명확히 분리된 책임을 가진 분리형 클라우드 네이티브 아키텍처를 사용합니다.
| Layer | Technology / Details |
|---|---|
| Frontend | React (Vite) + Material‑UI. UI 렌더링 및 클라이언트 측 암호화 작업(암호화/복호화)을 처리합니다. |
| Backend | Node.js + Express, 하이브리드 아키텍처를 따릅니다. |
| Local Development | 완전 Docker화된 단일 컨테이너로, 프로덕션 의존성을 반영하는 일관된 환경을 보장합니다. |
| Production Deployment | Vercel에 무상태 서버리스 함수로 배포되어, API가 유휴 시에 0으로 스케일링(비용 효율)되면서도 단일 Express 코드베이스를 유지합니다. |
| Database | MongoDB Atlas – 암호화된 메타데이터와 암호문을 저장합니다. |
| Authentication | Clerk – 신원 관리를 위임함으로써 인증 흐름(MFA, 세션 관리)의 공격 표면을 감소시킵니다. |
| Storage | Supabase Storage – 서명된 URL을 통해 바이너리 객체(이미지, PDF)를 격리하는 데만 사용됩니다. |
Source:
Security by Design: The Zero‑Trust Vault
보안은 선택적인 기능이 아니라 기본적인 아키텍처 제약이었습니다.
1. 정적 키의 문제
초기 설계에서는 서버 환경 변수(VITE_SECRET_KEY)에 저장된 정적 암호화 키를 사용했습니다. 곧 이것이 치명적인 결함이라는 것을 깨달았습니다. 공격자나 손상된 호스팅 환경이 환경 변수를 노출하면 모든 사용자의 데이터를 복호화할 수 있기 때문입니다. 키가 보여지는 상태였으며, Zero‑Trust의 핵심 개념을 위배했습니다.
2. 해결책: 사용자 파생 암호화
이를 해결하기 위해 정적 키를 완전히 제거하고 PBKDF2 (Password‑Based Key Derivation Function 2) 를 클라이언트 측에서 구현했습니다.
- 사용자가 로그인하면 Vault Password 를 입력합니다. 이 비밀번호는 전송되거나 저장되지 않으며, 클라이언트 메모리 내에서만 일시적으로 존재합니다.
- 브라우저가 PBKDF2를 실행해 메모리 내에서 임시 256‑bit AES 키를 파생합니다.
- 이 키가 네트워크 요청이 생성되기 전에 노트, 제목, 파일 경로를 암호화합니다.
서버는 암호문만을 보고(그리고 저장)합니다. 데이터베이스 관리자(저)가 데이터를 살펴보더라도 읽을 수 없는 문자열만 보게 됩니다.
PBKDF2 매개변수는 무차별 대입 공격에 대한 저항성과 저성능 클라이언트 디바이스에서 허용 가능한 지연 시간 사이의 균형을 맞추도록 선택되었습니다.
3. 미디어에 대한 일시적 접근
파일 저장을 위해 공개 버킷을 전혀 사용하지 않았습니다.
- 암호화된 경로: 데이터베이스에는 파일 경로를 가리키는 암호화된 문자열이 저장됩니다(예:
"user/123/image.jpg"가 암호화됨). - 요청 시 접근: 사용자가 금고를 잠금 해제하면 클라이언트가 경로를 복호화하고 Supabase에 Signed URL 을 요청합니다.
- 시간 제한: 해당 URL은 정확히 60 초 동안만 유효하며 그 후 만료됩니다. 이는 “링크 공유”에 의한 누수를 방지하고, URL이 가로채지더라도 거의 즉시 무용지물이 되도록 합니다.
인프라 진화: 비용 문제 해결
가장 가치 있는 학습 경험 중 하나는 인프라를 실제 비용 제약에 맞게 조정하면서 얻었습니다.
Phase 1: The “Enterprise” Trap (GCP)
초기 배포는 Google Cloud Platform의 Cloud Run과 Cloud Build를 사용했습니다. 이는 업계 표준 “엔터프라이즈” 설정이었지만, 개인 프로젝트에서는 큰 문제를 야기했습니다:
- High Costs: 로드 밸런서, 컨테이너‑레지스트리 저장소, 그리고 컴퓨팅 시간에 대한 비용이 빠르게 누적되었습니다.
- Complexity: 간단한 앱에 대한 IAM 역할 및 빌드 트리거 관리는 과도했습니다.
Phase 2: The Hybrid “Serverless Monolith” (Vercel)
Phase 2 내용은 여기서 계속됩니다 – 앱을 단일 코드베이스로 리팩터링하여 Vercel의 서버리스 함수로 배포하고, 비용 효율적인 스케일링 모델을 활용하면서 동일한 개발 워크플로를 유지한 방법을 설명합니다.
주요 내용
- Zero‑Trust by Design: 평문 키를 절대 저장하거나 전송하지 말고; 클라이언트 측에서 파생하고 메모리 내에만 보관하십시오.
- Cost‑Effective Architecture: 단일 Express 코드베이스로 로컬 Docker 개발과 Vercel 서버리스 프로덕션을 모두 제공할 수 있어 운영 오버헤드를 크게 줄입니다.
- Ephemeral Media Access: 짧은 TTL을 가진 서명된 URL은 사용성을 해치지 않으면서 바이너리 자산을 보호합니다.
InfoStuffs는 프라이버시 우선의 클라우드 네이티브 애플리케이션이 개인 개발자와 소규모 프로젝트에서도 실현 가능함을 보여줍니다.
InfoStuffs – 제로‑비용 서버리스 모놀리식
개요
배포 비용을 없애기 위해 스택을 월 $0 로 동작하도록 재구성했습니다:
| 환경 | 접근 방식 |
|---|---|
| 로컬 개발 (Docker화) | docker‑compose up 하나만 실행하면 프론트엔드, 백엔드, 데이터베이스 서비스가 모두 올라갑니다. 이를 통해 개발 환경을 격리하고 어느 머신에서든 재현할 수 있습니다. |
| 프로덕션 배포 (서버리스) | Express 앱을 영구 실행 컨테이너 대신 Vercel Serverless Functions에서 동작하도록 리팩터링했습니다. |
결과: 서버리스 모놀리식 – 전통적인 모놀리식처럼 개발하고(디버깅 용이, Docker 기반 로컬 실행) 배포 시에는 분산된 함수 형태로 운영합니다. 이렇게 하면 인프라 관리 제로와 개인 사용 시 비용 제로라는 두 마리 토끼를 모두 잡을 수 있습니다.
저는 아직 도메인이 여러 개의 경계 컨텍스트를 정당화하지 못하기 때문에 마이크로서비스를 의도적으로 피했습니다; 조기 분해는 복잡성을 높이고 공격 표면을 확대하지만 실질적인 이점은 없습니다.
Technical Challenges & Solutions
1. Environment Variable Visibility
.env 파일에 의존해 보안을 구현하는 것이 문제를 일으켰습니다. 사용자 파생 키로 마이그레이션하면서 “비밀번호 분실”과 같은 엣지 케이스가 발생했습니다.
Solution: 사용자가 복구 불가능한 데이터를 삭제하고 새로 시작할 수 있는 “Nuclear Reset” 기능을 구현하여 데이터 복구보다 보안을 우선시했습니다.
2. Monorepo Build Contexts
Vercel이 처음에는 monorepo 내부에 있는 vite.config.js를 찾지 못했습니다.
Solution: Vercel 설정에서 Root Directory를 명시적으로 지정하고, 빌드 명령을 재작성하여 올바른 경로에서 의존성을 설치하도록 했습니다.
3. Docker vs. Serverless Routing Mismatch
| Problem | Details |
|---|---|
| Local Docker | Express가 모든 라우팅을 내부에서 처리했습니다. |
| Vercel | API를 정적 파일로 취급했으며, /api/info/nuke와 같은 서브 경로 요청이 Express에 도달하기 전에 Vercel의 404 핸들러에 의해 차단되어 브라우저에서 CORS 오류가 발생했습니다. |
Solution: Hybrid Routing Strategy를 도입했습니다:
- Vercel 호환 진입점(
api/info.js)을 추가했습니다. vercel.json재작성 규칙을 설정해 모든 서브 라우트 트래픽을 직접 Express 인스턴스로 전달하도록 했습니다.
이 브리지는 동일한 코드를 로컬 Docker 환경과 Vercel Function 프로덕션 환경 모두에서 실행하게 하여 CORS 문제를 해결했습니다.
향후 로드맵
- Redis Caching – 자주 접근하는 (암호화된) 메타데이터에 대한 데이터베이스 읽기를 감소시킵니다.
- React Native Mobile App – 기존 로직을 감싸서 비밀번호 입력 대신 생체 인증(Face ID)으로 금고 잠금을 해제할 수 있게 합니다.
- Offline Mode – PWA 기능을 활용하여 캐시된 암호화된 노트를 읽기 전용으로 접근할 수 있게 합니다.
Closing Thoughts
InfoStuffs는 단순한 메모 앱이 아니라 Zero‑Trust Engineering에 대한 실용적인 탐구입니다. 데이터 가시성과 클라우드 비용이라는 실제 문제를 해결함으로써, 나는 프라이버시가 정책이 아니라 수학적으로 강제되는 시스템을 구축했습니다. 이는 실제 사용자 요구를 충족시키는 동시에 풀스택 보안 및 DevOps에 대한 귀중한 학습 경험을 제공합니다.
- Repository:
- Live Deployment:
Note: 라이브 배포는 인증 및 금고 비밀번호가 필요합니다. 핵심 보안 속성은 클라이언트 측에서 강제되며 위의 아키텍처 논의를 통해 가장 잘 이해할 수 있습니다.

