// security & threat model
Designed to be published, not hidden.
h4Bin follows Kerckhoffs's principle: everything about the system is public — this page, the source code, the wire format — and security rests entirely on keys generated in your browser. Here is the complete design, including what it cannot do.
Architecture in one paragraph
There is no backend. The site is static files; the application runs entirely in your tab.
A paste is deflate-compressed, encrypted, and encoded into the URL fragment (after
#). Fragments are consumed by the browser and
never included in HTTP requests (RFC 9110 §4.2.5); the site additionally
sends Referrer-Policy: no-referrer so links never leak through referrers.
Opening a link runs the same machinery in reverse, locally.
Cryptographic specification (format v1)
link = https://<host>/#v1.<payload>.<key>
payload = base64url( header ‖ ciphertext )
key = base64url( urlKey ) # 32 CSPRNG bytes
header (30 bytes, bound as AES-GCM AAD):
[0] version = 0x01
[1] flags — bit0: password required
[2..17] salt — 16-byte PBKDF2 salt
[18..29] iv — 12-byte AES-GCM nonce
kdf = PBKDF2-HMAC-SHA-256( urlKey ‖ UTF-8(password), salt, 600 000 ) → 256-bit key
cipher = AES-256-GCM( key, iv, deflate-raw(document JSON), AAD = header ), 128-bit tag
document = { v:1, content, lang, title?, created, expires? }
All primitives are the browser's native WebCrypto and Compression Streams — the cryptographic path imports zero third-party code.
Why this holds up
-
256-bit keyspace. The link key comes from
crypto.getRandomValues. Reading the source gives an attacker nothing better than brute-forcing 2256 keys. - One key, one message. Every paste generates a fresh key, salt and nonce, so no AES-GCM key ever encrypts twice — the classic nonce-reuse failure mode is eliminated by construction, not by discipline.
- Passwords are a real second factor. The password is mixed into the KDF input. Someone who intercepts the link must grind 600,000 PBKDF2-SHA-256 iterations per guess, offline, with no rate-limiting server to blame and no oracle to query.
- Tamper- and downgrade-proof. The header (version, flags, salt, nonce) is authenticated as AAD. Flip any bit — say, the "password required" flag — and decryption fails loudly. This is covered by an exhaustive bit-flip test matrix in CI.
- Cross-implementation verified. The test suite pins known-answer vectors produced by an independent implementation of the format (Node's OpenSSL-backed crypto), so the wire format cannot drift silently.
-
Defense in depth at the delivery layer. A strict Content-Security-Policy
(
connect-src 'none', no inline scripts, self-only sources) means even a hypothetical injected script has no way to exfiltrate; plus HSTS,frame-ancestors 'none',nosniff, and no-referrer.
Threat model — honestly
| Adversary | Outcome |
|---|---|
| Hosting provider / network observer | Sees static-asset requests only. Never sees fragments, so never sees pastes or keys. |
| Someone without the link | Nothing exists to attack — there is no listing, no database, no enumeration surface. |
| Someone with the link, paste password-sealed | Offline PBKDF2 grind at 600k iterations per guess. Strong passwords are effectively out of reach. |
| Ciphertext tampering / truncation | AES-GCM authentication fails; h4Bin shows an integrity error and renders nothing. |
| Someone you shared the full link with (no password) | Can read the paste — that is the point of sharing. Choose recipients accordingly. |
| Link leakage: browser history, messenger logs, screenshots, shoulder surfing | Outside any pastebin's control. The link is the secret — handle it like one. |
| Compromised endpoint (malware, hostile extension) | No web cryptography survives a compromised device. h4Bin is not an endpoint-security tool. |
| A malicious deployment serving altered code | Mitigated, not eliminated: the code is open source, builds are reproducible from the public repo, and you can self-host or build locally. Web apps cannot fully solve code-delivery trust. |
About expiry — advisory by design
An expiry date is sealed inside the encrypted payload. Conforming viewers (including this one) check it after decryption and refuse to display an expired paste. Because the ciphertext is authenticated, the date cannot be stripped or altered. But be clear about what this is: data inside a link cannot be remotely destroyed, and a determined link-holder could modify their own client to ignore the check. Expiry is a strong hint honored by honest software — not deletion. Services that promise "burn after reading" without holding your data server-side are promising something they cannot deliver; h4Bin refuses to make that promise.
Verify everything yourself
- Read the code: github.com/h4rithd/h4bin — the crypto lives in
src/lib/, ~400 lines, dependency-free. - Run the tests:
git clone,npm ci,npm test— including the tamper matrix and cross-implementation vectors. - Watch the network tab while minting or opening a paste: zero requests.
- Check the headers:
curl -sI https://bin.h4rithd.com— CSP, HSTS, no-referrer, nosniff. - Distrust us entirely: build locally with
npm run buildand servedist/yourself.
Reporting a vulnerability
Found something? Please report it privately via GitHub Security Advisories. Non-sensitive issues are welcome on the issue tracker. Reports are taken seriously and credited.
format: v1 · kdf: pbkdf2-sha256/600k · cipher: aes-256-gcm · last updated: 2026-07-10