- Set Spanish as default language with ephemeral/encrypted privacy focus - Translate all user-facing strings and legal pages to Spanish - Replace Norwegian flag with Spanish flag in footer - Remove Hemmelig/terces.cloud links, add cloudhost.es sponsorship - Rewrite PrivacyPage: zero data collection, ephemeral design emphasis - Rewrite TermsPage: Spanish law, RGPD, paste.es/CloudHost.es references - Update PWA manifest, HTML meta tags, package.json branding - Rename webhook headers to X-Paste-Event / X-Paste-Signature - Update API docs title and contact to paste.es / cloudhost.es Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
6.3 KiB
Encryption
Hemmelig uses a zero-knowledge architecture where all encryption and decryption happens entirely in your browser. The server never sees your plaintext secrets or encryption keys.
How It Works
- Secret Creation: When you create a secret, it's encrypted in your browser before being sent to the server
- Key Transmission: The decryption key is passed via URL fragment (
#decryptionKey=...), which is never sent to the server - Secret Retrieval: When viewing a secret, the encrypted data is fetched and decrypted locally in your browser
Why URL Fragments?
The decryption key is placed in the URL fragment (the part after #) for a critical security reason:
URL fragments are never transmitted to servers.
When you visit a URL like https://example.com/secret/abc123#decryptionKey=xyz:
- The browser sends a request to
https://example.com/secret/abc123 - The fragment (
#decryptionKey=xyz) stays in your browser - Server logs, proxies, load balancers, and CDNs never see the fragment
- The key exists only in the browser's address bar and JavaScript
This is defined in RFC 3986 and is a fundamental behavior of all web browsers.
What This Means
| Component | Sees the Key? |
|---|---|
| Your browser | ✅ Yes |
| Hemmelig server | ❌ No |
| Reverse proxies (nginx, etc.) | ❌ No |
| CDNs (Cloudflare, etc.) | ❌ No |
| Server access logs | ❌ No |
| Network monitoring tools | ❌ No |
Note: Be aware that browser history and bookmarks store the full URL including fragments.
Technical Details
Why This Encryption?
Hemmelig uses AES-256-GCM via the Web Crypto API for several important reasons:
- Browser-native: The Web Crypto API is built into all modern browsers. No external libraries required.
- Hardware-accelerated: AES is supported by dedicated instructions (AES-NI) in most modern CPUs (Intel, AMD, ARM), making encryption and decryption fast.
- Battle-tested: AES-256 is a NIST-approved standard.
- Authenticated encryption: GCM mode provides both confidentiality and integrity, detecting any tampering with the ciphertext.
- No dependencies: By using native browser APIs, we avoid supply chain risks from third-party cryptography libraries.
Algorithm
- Encryption: AES-256-GCM (Galois/Counter Mode)
- Key Derivation: PBKDF2 with SHA-256
- Implementation: Web Crypto API (browser-native)
Parameters
| Parameter | Value | Description |
|---|---|---|
| Algorithm | AES-GCM | Authenticated encryption with associated data |
| Key Length | 256 bits | Maximum AES key size |
| IV Length | 96 bits (12 bytes) | Initialization vector, randomly generated per encryption |
| Salt Length | 32 characters | Unique per secret, stored server-side |
| PBKDF2 Iterations | 1,300,000 | Key derivation iterations |
| PBKDF2 Hash | SHA-256 | Hash function for key derivation |
Encryption Process
- Key Generation: A 32-character random key is generated using
nanoid, or a user-provided password is used directly - Key Derivation: PBKDF2 derives a 256-bit AES key from the password/key and a unique salt
- Encryption: AES-256-GCM encrypts the plaintext with a random 96-bit IV
- Output Format:
IV (12 bytes) || Ciphertext
Password Protection
When you set a password on a secret:
- The password is used directly as the encryption key instead of a randomly generated key
- The URL does not include the
#decryptionKey=...fragment - The recipient must enter the password manually to decrypt the secret
- This allows you to share the URL and password through separate channels for additional security
Decryption Process
- Parse: Extract the 12-byte IV from the beginning of the encrypted data
- Key Derivation: PBKDF2 derives the same AES key using the password/key and salt
- Decryption: AES-GCM decrypts and authenticates the ciphertext
Security Properties
- Confidentiality: AES-256 provides strong encryption
- Integrity: GCM mode provides authenticated encryption, detecting any tampering
- Key Strength: PBKDF2 with 1,300,000 iterations provides resistance against brute-force attacks
- Forward Secrecy: Each secret uses a unique salt and random IV
File Encryption
Files are encrypted using the same AES-256-GCM scheme. The file buffer is encrypted directly, and the output format is identical: IV || Ciphertext.
What the Server Stores
- Encrypted secret (ciphertext)
- Salt (used for key derivation)
- Metadata (expiration, view count, etc.)
What the Server Never Sees
- Plaintext secrets
- Encryption keys or passwords
- Decryption keys (passed via URL fragment)
References
- MDN Web Crypto API - Browser-native cryptography documentation
- MDN AES-GCM - AES-GCM algorithm parameters
- MDN PBKDF2 - PBKDF2 key derivation parameters
- OWASP Cryptographic Storage Cheat Sheet - Best practices for cryptographic storage
- OWASP Password Storage Cheat Sheet - Key derivation recommendations
- NIST SP 800-132 - Password-Based Key Derivation (current version)
- NIST SP 800-132 Revision Proposal - Upcoming revision with memory-hard functions
- NIST AES Specification - Official AES standard (FIPS 197)
- Crypto 101 - Free introductory course on cryptography