Vaulted의 zero-knowledge 암호화를 직접 검증해
보안 도구를 믿지 말고 직접 검증해. 브라우저 테스트 4개, 설치 없음, 5분.
60초 체크리스트
먼저 훑어봐. 아래 상세 안내에서 각 단계를 깊이 다뤄.
- 1
DevTools 열기 → Network 탭
Vaulted에서 Cmd+Option+I (macOS) 또는 F12 (Windows/Linux)를 눌러. Network 탭으로 전환하고 Fetch/XHR으로 필터링해.
- 2
시크릿 만들고 POST 검사하기
알아볼 수 있는 카나리 문자열(예: CANARY-12345)을 입력하고 Create를 클릭해. POST /api/secrets 요청을 찾아. Payload 탭을 열면 — 암호문(base64)과 iv만 보여. 본문에서 카나리를 검색해보면 없어.
- 3
공유 링크 검사하기
생성된 링크를 봐. 복호화 키는 # 뒤(URL 프래그먼트)에 있어. RFC 3986에 따라 브라우저는 프래그먼트를 서버에 절대 보내지 않아 — 요청 라인, 헤더, 리퍼러 어디에도 없어.
- 4
복호화가 클라이언트 사이드에서 실행되는지 확인하기
새 탭에서 링크를 열어. /api/secrets/[id]의 GET 응답은 암호문만 반환해. Sources 패널에서 crypto.subtle.decrypt를 검색하면 — 그 호출은 우리 서버가 아닌 네 브라우저에서 실행돼.
무엇을 검증하는 건지
Vaulted의 주장은 구체적이야: 서버는 평문도, 암호화 키도 절대 보지 않아. 이건 네 가지 관찰 가능한 속성으로 나눌 수 있어:
- 평문은 어떤 네트워크 요청에도 나타나지 않아.
- 암호화 키는 어떤 네트워크 요청에도 나타나지 않아.
- URL 프래그먼트(키가 있는 곳)는 서버에 절대 도달하지 않아.
- 서버 사이드 데이터만으로는 시크릿을 복원할 수 없어.
네 가지 모두 성립하면, 아키텍처는 구조적으로 zero-knowledge야 — 마케팅 언어가 필요 없어.
테스트 1 — 평문은 네트워크를 절대 통과하지 않아
- vaulted.fyi를 열어.
- DevTools를 열고 Network 탭으로 전환해.
Fetch/XHR으로 필터링해. - 시크릿 필드에 알아볼 수 있는 문자열을 입력해 — 예를 들어
CANARY-12345같은 거. - Create를 클릭해.
단일 POST 요청이 /api/secrets으로 가는 걸 볼 수 있어. 클릭하고 Payload 탭을 봐.
본문에는 ciphertext 필드와 iv 필드가 있어. 둘 다 base64url 인코딩된 블롭이야. 페이로드에서 CANARY-12345를 검색해봐 — 없어. 평문은 요청이 전송되기 전에 브라우저에서 암호화됐어.
철저하게 확인하고 싶으면 네트워크 스로틀링을 "Slow 3G"로 설정하고 반복해봐. 암호문은 여전히 노이즈처럼 보이고, 평문은 여전히 어디에도 없어.
테스트 2 — 키도 네트워크를 절대 통과하지 않아
시크릿을 만들면 Vaulted가 공유 링크를 보여줘. 이런 형태야:
https://vaulted.fyi/s/abc123#dGhpcyBpcyBhIGtleQ
^^^^^^^^^^^^^^^^^^^^
encryption key (base64url)# 뒤에 있는 부분이 URL 프래그먼트야. RFC 3986에 따라, 브라우저는 프래그먼트를 로컬에서 처리해 — HTTP 요청 라인, 헤더, 리퍼러 어디에도 나타나지 않아.
직접 증명해봐:
- 공유 링크를 복사해.
- DevTools의 Network 탭을 열고 새 탭에서 링크를 열어.
GET /s/abc123요청을 찾아.- Headers 탭을 봐 — 전체 요청 URL, 프래그먼트 없음.
- Request 탭을 봐 — 프래그먼트 없음.
- 다음 페이지에서
document.referrer를 확인해 — 프래그먼트 제거됨.
수신자가 시크릿을 복호화하는 데 필요한 키는 URL에 있었지만, 그들의 브라우저 안에 머물렀어. 서버는 GET /s/abc123만 보고 그 이상은 없었어.
테스트 3 — 서버 단독으로는 복호화할 수 없어
터미널을 열고 시크릿을 직접 curl로 가져와:
curl https://vaulted.fyi/api/secrets/abc123ciphertext와 iv를 포함한 JSON을 받게 돼. 네 시크릿의 전체 서버 사이드 상태가 이게 다야.
이제 이걸 해석해봐. URL 프래그먼트의 키 없이는 불가능해. 제대로 생성된 랜덤 키로 만든 AES-256-GCM은 실용적인 관점에서 랜덤 노이즈와 구별할 수 없어. Vaulted 서버의 전체 데이터베이스 덤프를 가져와도 이것만 나와 — 암호화된 블롭과 메타데이터.
이게 zero-knowledge의 문자 그대로의 정의야: 서버는 읽을 수 없는 데이터를 저장해.
테스트 4 — 암호화는 네가 볼 수 있는 JavaScript에서 일어나
Vaulted는 Web Crypto API를 사용해, 브라우저 내장 기능이야 — 작업을 숨기는 난독화된 WASM이나 네이티브 모듈이 없어. 암호화가 일어나는 걸 직접 볼 수 있어.
- DevTools에서 Sources 탭을 열어.
crypto.subtle.encrypt를 검색해 (Cmd+Option+F로 전역 검색).crypto.subtle.encrypt를 호출하는 줄에 중단점을 설정해.- 새 시크릿 생성을 트리거해.
- 중단점이 걸려. 인수를 검사해: 평문이
Uint8Array로, AES-GCM 키와 새 랜덤 IV를 볼 수 있어. - 호출을 넘어가봐. 결과가 전송되는 암호문이야.
지금 네 브라우저에서, 어떤 네트워크 요청 전에, 평문이 암호문으로 변하는 걸 직접 보고 있어. 데이터가 다른 경로를 취할 수는 없어.
이게 증명하는 것
- 평문은 브라우저 밖으로 나가지 않아. 테스트 1.
- 키는 브라우저 밖으로 나가지 않아. 테스트 2.
- 서버는 저장된 것만으로 복호화할 수 없어. 테스트 3.
- 암호화는 실제이고 클라이언트 사이드에서 일어나. 테스트 4.
이게 전체 zero-knowledge 주장이야, 5분 안에 실제 프로덕션 시스템에서 직접 확인한 거야.
이게 증명하지 못하는 것
검증은 정직한 작업이야 — 한계를 명확히 하는 게 가치 있어.
- 테스트는 지금 이 순간의 동작을 증명해. 향후 코드 변경이 이 속성을 깨뜨릴 수 있어. 지속적인 확신이 필요하면 변경 로그를 구독하고 주기적으로 테스트를 다시 실행해.
- 네 기기를 보호하지는 않아. 키로거나 악성 브라우저 확장 프로그램은 복호화 후 평문을 읽을 수 있어. 이건 어떤 웹 앱의 통제 범위도 벗어나.
- 네가 실행한 JavaScript가 Vaulted가 제공하는 JavaScript라고 가정해. CDN을 장악한 표적 공격자는 특정 사용자에게 다른 번들을 제공할 수 있어. Subresource Integrity와 재현 가능한 빌드가 이를 완화해. 전체 내용은 위협 모델을 참조해.
더 깊이 파고들기
더 자세히 알고 싶으면:
- 전체 암호화 구현을 위한 zero-knowledge 암호화 심층 분석을 읽어봐.
- AES-256-GCM을 단계별로 살펴보려면 Encryption Playground를 사용해봐.
- 서버 사이드 암호화와 비교해 어떤 모델이 네 위협 모델에 맞는지 결정해.
보안 도구에 대한 가장 강력한 논거는 마케팅이 아니야. 직접 분해해서 하는 일을 확인할 수 있다는 거야. Vaulted는 그렇게 할 수 있도록 만들어졌어.