Sanctum: 암호학적으로 부인 가능한 볼트 시스템과 IPFS 스토리지
Source: Dev.to

🎭 The Problem: Encryption Isn’t Enough
Traditional encrypted storage has a fatal flaw:
Attacker: "Give me the password or else."
You: "I don't have one."
Attacker: *checks encrypted file* "This is clearly encrypted. Try again."
You can’t prove the absence of data—until now.
✨ 해결책: 암호학적 부인 가능성
Sanctum은 세 개의 구분할 수 없는 레이어를 생성합니다:
| 레이어 | 설명 |
|---|---|
| 디코이 레이어 | 무해한 내용 (가족 사진, $200이 들어 있는 작은 지갑) |
| 숨겨진 레이어 | 실제 비밀 (고발자 문서, 주요 암호화 지갑) |
| 패닉 레이어 | 압박 상황에서 “볼트가 삭제됨”을 표시 |
마법? 세 레이어 모두 암호학적으로 동일합니다. 공격자는 어느 레이어가 실제인지—또는 숨겨진 레이어가 존재하는지조차 증명할 수 없습니다.
🏗️ 아키텍처: 설계 단계에서 제로‑트러스트
클라이언트‑사이드 암호화 흐름
// 1. User creates vault with decoy + hidden content
const decoyBlob = encrypt(decoyContent, ''); // Empty passphrase
const hiddenBlob = encrypt(hiddenContent, deriveKey(passphrase));
// 2. XOR both layers (makes them indistinguishable)
const combined = xor(decoyBlob, hiddenBlob);
// 3. Upload to IPFS
const decoyCID = await ipfs.upload(decoyBlob);
const hiddenCID = await ipfs.upload(hiddenBlob);
// 4. Split‑key architecture
const keyA = randomBytes(32); // Stays in URL
const keyB = randomBytes(32); // Encrypted in database
const vaultURL = `https://sanctumvault.online/unlock/${vaultId}#${encode(keyA)}`;
기술 스택
- 프론트엔드: Next.js 15 + React + Web Crypto API
- 암호학: XChaCha20‑Poly1305 + Argon2id (256 MB 메모리, 3회 반복)
- 스토리지: IPFS via Pinata / Filebase (무료 티어)
- 데이터베이스: Cloudflare D1 (키 분할 저장 전용)
- 호스팅: Cloudflare Pages (정적 사이트)
보안 기능
// RAM‑only storage (no disk persistence)
class SecureStorage {
private keys = new Map();
store(key: string, value: Uint8Array): void {
this.keys.set(key, value);
// Auto‑clear after 5 minutes
setTimeout(() => this.wipe(key), 300_000);
}
wipe(key: string): void {
const data = this.keys.get(key);
if (data) {
data.fill(0); // Overwrite memory
this.keys.delete(key);
}
}
}
// Panic key: Double‑press Escape
let escapeCount = 0;
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
escapeCount++;
if (escapeCount === 2) {
wipeAllKeys();
window.location.href = '/';
}
setTimeout(() => (escapeCount = 0), 500);
}
});
🎯 실제 사용 사례
-
기자, 출처 보호
Decoy: Published articles, public research notes Hidden: Confidential source documents, whistleblower communications Scenario: Device seized at border → reveal decoy, sources stay protected -
압박을 받는 암호화폐 보유자
Decoy: Small wallet with $200 ("this is all I have") Hidden: Main wallet with life savings Scenario: $5 wrench attack → hand over decoy wallet, real funds stay safe -
권위주의 정권 내 활동가
Decoy: Personal photos, innocuous social media content Hidden: Protest coordination plans, evidence of government abuse Scenario: Police raid → show decoy layer, cannot prove hidden content exists
🛡️ 공격 저항
| 공격 벡터 | 방어 |
|---|---|
| Physical Duress | 위장 구문을 공개; 숨겨진 레이어는 구별할 수 없음. |
| Disk Forensics | RAM‑only 저장; 키가 디스크에 기록되지 않음; 탭 닫을 때 자동 삭제. |
| Timing Analysis | 모든 작업에 500‑2000 ms 무작위 지연 적용. |
| Blob Size Analysis | 표준 크기(1 KB, 10 KB, 100 KB, 1 MB, 10 MB, 25 MB)로 패딩. |
| Brute Force | 256 MB 메모리를 사용하는 Argon2id로 무차별 공격이 실질적으로 불가능. |
🚀 Quick Start
For Users
Visit sanctumvault.online
- Pinata 또는 Filebase (무료 IPFS 제공자) 설정
- 선택적 미끼 콘텐츠와 함께 금고 생성
- 숨겨진 레이어를 위한 암호문 설정
- 링크 공유 – 암호문은 본인만 알 수 있음
For Developers
# Clone repository
git clone https://github.com/Teycir/Sanctum.git
cd Sanctum
# Install dependencies
npm install
# Run development server
npm run dev
🔬 기술 심층 분석
왜 XChaCha20‑Poly1305인가?
// AES‑GCM: 96‑bit nonce (collision risk after 2^48 encryptions)
// XChaCha20: 192‑bit nonce (collision risk after 2^96 encryptions)
import { xchacha20poly1305 } from '@noble/ciphers/chacha';
export function encrypt(
plaintext: Uint8Array,
key: Uint8Array
): Uint8Array {
const nonce = randomBytes(24); // 192‑bit nonce
const cipher = xchacha20poly1305(key, nonce);
return cipher.encrypt(plaintext);
}
XChaCha20‑Poly1305는 훨씬 더 큰 논스 공간을 제공하여 논스 재사용 문제를 없애고 브라우저에서 높은 성능을 갖춘 인증된 암호화를 제공합니다.
// Encryption function
function encryptData(
plaintext: Uint8Array,
key: Uint8Array
): EncryptionResult {
const nonce = randomBytes(24); // 192‑bit nonce
const cipher = xchacha20poly1305(key, nonce);
const ciphertext = cipher.encrypt(plaintext);
return {
ciphertext,
nonce,
// Authenticated encryption tag (last 16 bytes)
authTag: ciphertext.slice(-16),
};
}
분할‑키 아키텍처
// KeyA: Stays in URL fragment (never sent to server)
// KeyB: Encrypted in database with vault‑specific key
const vaultKey = deriveKey(vaultId + salt);
const encryptedKeyB = encrypt(keyB, vaultKey);
// To decrypt IPFS CIDs:
const masterKey = xor(keyA, keyB);
const decoyCID = decrypt(encryptedDecoyCID, masterKey);
const hiddenCID = decrypt(encryptedHiddenCID, masterKey);
30‑일 유예 기간
-- Stage 1: Soft delete (mark inactive)
UPDATE vaults
SET is_active = 0
WHERE expires_at < NOW();
-
✅ 받지 않음:
- 국가 안보 서한 (NSLs)
- FISA 법원 명령
- 검열 명령
- 백도어 구현 요청
-
✅ 아키텍처 보장:
- 제로‑지식: 사용자 금고를 복호화할 수 없음
- 사용자 로그 없음: IP 주소나 메타데이터가 없음
- 백도어 없음: 모든 코드가 오픈‑소스
- RAM‑전용: 키를 영구 저장하지 않음
🌐 왜 IPFS인가?
Traditional cloud storage has single points of failure:
| Issue | Cloud Storage | IPFS |
|---|---|---|
| 중앙 집중식 | 제공자가 데이터를 넘겨주도록 강제될 수 있음 | 데이터가 다수의 노드에 복제됨 |
| 검열 가능 | 정부가 접근을 차단할 수 있음 | 콘텐츠 주소 지정(CID), 위치 기반이 아님 |
| 삭제 가능 | 제공자가 귀하의 데이터를 삭제할 수 있음 | 불변 – 업로드 후 수정 불가 |
| 비용 | 지속적인 요금 | 무료 티어: Pinata (1 GB) + Filebase (5 GB) |
🚫 Sanctum 이 아닌 것
- ❌ 비밀번호 관리자 – 이를 위해 KeePassXC / Bitwarden를 사용하세요
- ❌ 백업 솔루션 – IPFS 데이터는 고정 해제될 수 있습니다
- ❌ 파일‑공유 서비스 – 링크는 영구적이며 삭제할 수 없습니다
- ❌ VPN – 익명을 위해 Tor Browser를 사용하세요
💡 배운 교훈
1. RAM‑Only Storage Is Hard
// ❌ WRONG: localStorage persists to disk
localStorage.setItem('key', encode(key));
// ✅ CORRECT: In‑memory only
const keyStore = new Map();
2. Timing Attacks Are Real
// ❌ WRONG: Instant response reveals wrong passphrase
if (passphrase !== correctPassphrase) {
return { error: 'Invalid passphrase' };
}
// ✅ CORRECT: Constant‑time comparison + random delay
const isValid = timingSafeEqual(hash(passphrase), hash(correctPassphrase));
await sleep(randomInt(500, 2000));
return isValid ? { data } : { error: 'Invalid passphrase' };
3. Browser History Is a Leak
// Vault URLs contain KeyA in fragment.
// Must clear from browser history.
if (window.history.replaceState) {
window.history.replaceState(null, '', '/unlock');
}
🔮 미래 로드맵
- Shamir Secret Sharing – 여러 사람에게 금고 접근을 분산
- Dead Man’s Switch – 비활성 상태 후 자동 해제
- Steganography – 무해해 보이는 이미지에 금고 숨기기
- Hardware Key Support – YubiKey / Ledger 통합
- Mobile Apps – iOS / Android 바이오메트릭 잠금 해제
🙏 감사의 글
- VeraCrypt – 그럴듯한 부인 가능성에 대한 영감
- Cloudflare Pages – 무료 정적 사이트 호스팅
- Pinata / Filebase – 무료 IPFS 고정 서비스
- @noble/ciphers – 검증된 암호학 라이브러리
📜 License
Business Source License 1.1 – 비생산 용도에 대해서는 무료입니다. 생산 용도로 사용하려면 4년 후에 상업 라이선스가 필요합니다.
🔗 링크
- 실시간 데모:
- GitHub:
- 비디오 데모:
- 연락처:
💬 토론
어떻게 생각하시나요? 민감한 데이터에 암호학적 부인성을 사용하시겠습니까? 또 어떤 사용 사례를 상상할 수 있나요?
아래에 댓글을 남기거나 GitHub에서 이슈를 열어 주세요!
❤️와 🔒으로 만든 개발자: Teycir Ben Soltane
면책 조항: Sanctum은 정당한 프라이버시 요구를 위한 도구입니다. 사용자는 현지 법규를 준수할 책임이 있습니다. 개발자는 불법 활동을 용인하지 않습니다.