Enkripsi Zero-Knowledge: Bagaimana Vaulted Menjaga Rahasiamu Tetap Privat
Oleh Maxim Novak
Enkripsi zero-knowledge berarti server tidak pernah melihat plaintext atau kunci enkripsimu. Vaulted menerapkan ini dengan mengenkripsi setiap rahasia di browsermu menggunakan AES-256-GCM sebelum data apa pun mencapai server. Server hanya menyimpan ciphertext — ia tidak memiliki akses ke kunci dekripsi, yang hanya ada di URL fragment.
Artikel ini menjelaskan implementasi kriptografis yang tepat: bagaimana kunci dibuat, bagaimana data dienkripsi, bagaimana kunci sampai ke penerima, dan apa yang terjadi saat kamu menambahkan passphrase. Setiap detail di sini mencerminkan kode aktual yang berjalan di browsermu.
Untuk gambaran umum mengapa enkripsi sisi klien penting, lihat artikel kami sebelumnya tentang enkripsi sisi klien. Artikel ini menggali lebih dalam ke dalam primitif kriptografis.
Pembuatan kunci
Setiap kali kamu membuat rahasia, Vaulted membuat kunci enkripsi AES-256-GCM baru menggunakan Web Crypto API:
crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, [
'encrypt',
'decrypt',
]);
Beberapa hal yang perlu diperhatikan:
- AES-GCM (Galois/Counter Mode) memberikan kerahasiaan sekaligus integritas — ia mendeteksi manipulasi, bukan hanya pembacaan yang tidak sah.
- Panjang kunci 256-bit adalah pilihan terkuat yang tersedia untuk AES dan digunakan oleh pemerintah dan lembaga keuangan di seluruh dunia.
- Extractable:
true— kunci dapat diekspor sebagai byte mentah agar bisa ditempatkan di URL fragment untuk penerima.
Web Crypto API mendelegasikan ke mesin kriptografis native browser (misalnya BoringSSL di Chromium, NSS di Firefox). Kunci dibuat dengan aman di memori dan tidak pernah menyentuh disk atau jaringan.
Enkripsi
Dengan kunci yang sudah dibuat, Vaulted mengenkripsi plaintextmu:
const iv = crypto.getRandomValues(new Uint8Array(12));
crypto.subtle.encrypt(
{ name: 'AES-GCM', iv },
key,
new TextEncoder().encode(plaintext),
);
Initialization vector (IV): AES-GCM membutuhkan IV acak 12-byte (96-bit) untuk setiap operasi enkripsi. IV tidak perlu dirahasiakan — ia disimpan bersama ciphertext — tetapi tidak boleh pernah digunakan ulang dengan kunci yang sama. Karena Vaulted membuat kunci baru untuk setiap rahasia, penggunaan ulang IV tidak mungkin terjadi secara desain.
Encoding: Baik ciphertext maupun IV diencode sebagai string base64url (base64 standar dengan + diganti -, / diganti _, dan = di akhir dihapus). Ini membuatnya aman untuk dimasukkan ke dalam URL dan payload JSON.
Ciphertext terenkripsi dan IV dikirim ke server untuk disimpan. Plaintext tidak pernah meninggalkan browser.
Pengiriman kunci via URL fragment
Setelah enkripsi, kunci diekspor dan ditempatkan di URL fragment — bagian setelah #:
const rawKey = await crypto.subtle.exportKey('raw', key);
// rawKey → base64url-encoded string
Tautan berbagi yang dihasilkan terlihat seperti ini:
https://vaulted.fyi/s/abc123#dGhpcyBpcyBhIGtleQ
^^^^^^^^^^^^^^^^^^^^^^^^
encryption key (base64url)
Pemisah # sangat krusial. Sesuai RFC 3986, fragment identifier diproses sepenuhnya oleh klien. Browser tidak pernah menyertakan fragment dalam permintaan HTTP — tidak di URL, tidak di header Referer, tidak di mana pun yang bisa dilihat server.
Ketika penerima membuka tautan:
- Browser meminta
/s/abc123dari server (tanpa fragment) - Server mengembalikan ciphertext terenkripsi dan IV
- JavaScript membaca fragment dari
window.location.hash - Kunci diimpor kembali ke Web Crypto API
- Ciphertext didekripsi secara lokal
Server memfasilitasi penyimpanan dan pengambilan blob terenkripsi. Ia tidak pernah berpartisipasi dalam operasi kriptografis.
Perlindungan passphrase opsional
Untuk rahasia yang membutuhkan perlindungan tambahan, Vaulted mendukung key wrapping berbasis passphrase. Ini menambahkan lapisan kedua: bahkan jika seseorang mencegat tautan, mereka tidak dapat mendekripsi rahasia tanpa passphrase.
Derivasi kunci dengan PBKDF2
Saat kamu menetapkan passphrase, Vaulted menurunkan wrapping key menggunakan PBKDF2:
crypto.subtle.deriveKey(
{
name: 'PBKDF2',
salt, // 16 bytes, randomly generated
iterations: 600_000,
hash: 'SHA-256',
},
keyMaterial, // the passphrase, imported as raw key material
{ name: 'AES-KW', length: 256 },
false,
['wrapKey', 'unwrapKey'],
);
- 600.000 iterasi membuat serangan brute-force sangat mahal secara komputasi.
- Salt acak 16-byte memastikan passphrase yang sama menghasilkan kunci turunan yang berbeda untuk setiap rahasia yang berbeda.
- SHA-256 adalah fungsi hash yang digunakan di setiap iterasi PBKDF2.
- Kunci turunan adalah kunci AES-KW (AES Key Wrap, RFC 3394), bukan kunci AES-GCM. Key wrapping adalah operasi yang berbeda dari enkripsi.
Membungkus kunci enkripsi
Kunci AES-KW yang diturunkan membungkus kunci enkripsi asli:
crypto.subtle.wrapKey('raw', encryptionKey, wrappingKey, 'AES-KW');
URL fragment kini berisi kunci yang telah dibungkus dan salt, dipisahkan oleh titik:
#wrappedKeyBase64url.saltBase64url
Membuka bungkusan saat dekripsi
Ketika penerima memasukkan passphrase, Vaulted membalik prosesnya:
- Pisahkan fragment pada
.untuk mendapatkan kunci yang telah dibungkus dan salt - Turunkan wrapping key AES-KW yang sama menggunakan PBKDF2 dengan passphrase dan salt
- Buka bungkusan kunci enkripsi dengan
crypto.subtle.unwrapKey - Dekripsi ciphertext dengan kunci AES-GCM yang telah dibuka bungkusnya
Jika passphrase salah, unwrapKey akan melempar error — tidak ada dekripsi parsial atau kebocoran informasi.
Alur dekripsi
Tanpa passphrase, dekripsi berjalan sederhana:
- Impor kunci dari URL fragment (
crypto.subtle.importKey) - Dekripsi ciphertext dengan IV (
crypto.subtle.decrypt) - Decode plaintext dari byte UTF-8
Dengan passphrase:
- Pisahkan fragment untuk mengekstrak kunci yang telah dibungkus dan salt
- Turunkan wrapping key dari passphrase dan salt (PBKDF2)
- Buka bungkusan kunci enkripsi (AES-KW)
- Dekripsi ciphertext (AES-GCM)
- Decode plaintext
Semua ini terjadi di browser. Server mengembalikan blob terenkripsi dan metadata (jumlah tampilan, waktu kedaluwarsa) — tidak lebih dari itu.
Model ancaman
Tidak ada alat keamanan yang melindungi dari segalanya. Berikut adalah ancaman yang dirancang untuk ditangani oleh arsitektur Vaulted, dan apa yang berada di luar cakupannya.
Yang dilindungi Vaulted
- Kompromi server — Server hanya menyimpan ciphertext terenkripsi. Tanpa kunci (yang tidak pernah dikirim ke server), data tidak bisa dibaca. Dump database lengkap hanya menghasilkan blob terenkripsi.
- Pelanggaran database — Sama seperti di atas. Ciphertext tanpa kunci tidak berguna secara komputasi dengan AES-256-GCM.
- Man-in-the-middle di sisi server — Kunci enkripsi ada di URL fragment, yang tidak pernah dikirim dalam permintaan HTTP. TLS melindungi ciphertext saat transit.
- Operator layanan membaca rahasia — Zero-knowledge berarti persis itu. Kami tidak bisa membaca rahasiamu bahkan jika dipaksa, karena kami tidak memiliki kuncinya.
Yang TIDAK dilindungi Vaulted
- Browser atau perangkat yang disusupi — Jika perangkat penerima memiliki malware atau keylogger, plaintext yang sudah didekripsi dapat dicuri setelah proses dekripsi.
- Ekstensi browser berbahaya — Ekstensi dengan akses halaman dapat membaca DOM setelah dekripsi, termasuk rahasia yang telah terungkap.
- Pencegatan tautan — URL berisi kunci enkripsi di fragment. Siapa pun yang mendapatkan URL lengkap (termasuk fragment) dapat mendekripsi rahasia. Bagikan tautan melalui saluran yang aman.
- Shoulder surfing — Begitu rahasia ditampilkan di layar, siapa pun yang melihat bisa membacanya.
- Perilaku penerima — Setelah dekripsi, penerima dapat mengambil tangkapan layar, menyalin, atau membagikan plaintext. Vaulted tidak bisa mencegah ini.
Fitur passphrase mengurangi risiko pencegatan tautan: bahkan dengan URL lengkap, penyerang tetap membutuhkan passphrase. Bagikan passphrase melalui saluran berbeda (panggilan telepon, pesan terpisah) untuk pertahanan berlapis.
Ringkasan
Enkripsi zero-knowledge Vaulted berarti:
- Kunci AES-256-GCM baru dibuat untuk setiap rahasia
- Enkripsi terjadi sepenuhnya di browsermu
- Server hanya menyimpan ciphertext terenkripsi
- Kunci dekripsi hanya berjalan di URL fragment, yang tidak pernah dikirim browser ke server
- Key wrapping passphrase opsional menambahkan lapisan kedua via PBKDF2 dan AES-KW
Hasilnya: bahkan jika setiap server, database, dan jalur jaringan disusupi sekaligus, rahasiamu tetap terenkripsi. Kunci hanya ada di tautan yang kamu bagikan.
Siap berbagi rahasia dengan aman? Buat rahasia di Vaulted — hanya butuh 10 detik.
Terkait
- Keamanan di Vaulted — ringkasan model keamanan kami
- Mengapa Enkripsi Sisi Klien Penting — alasan mengenkripsi di browser
- Encryption Playground — lihat enkripsi AES-256-GCM langkah demi langkah