h4Bin

// security & threat model

Security explained, step by step.

h4|Bin follows Kerckhoffs's principle: security rests on keys generated in your browser, not on hiding the cryptographic design. This page documents the complete data flow, wire format, protections, verification steps, and limitations without publishing the private source code.

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 v2 — current)

link     = https://<host>/#v2.<blob>
blob     = base64url( urlKey ‖ payload )            # urlKey: 32 CSPRNG bytes at fixed offset
payload  = header ‖ ciphertext

header (2 bytes, bound as AES-GCM AAD):
  [0] version = 0x02
  [1] flags   - bit0: password required

key      = password ? PBKDF2-HMAC-SHA-256( password, salt = urlKey, 600 000 )
                    : urlKey                        # full-entropy 256-bit key, used raw
iv       = 12 zero bytes                            # safe: every key seals exactly one message
cipher   = AES-256-GCM( key, iv, deflate-raw(document), AAD = header ), 128-bit tag

document (binary):
  docflags(1) ‖ created(u32) ‖ [expires u32] ‖ [len,lang] ‖ [len,title] ‖ content

v2 stores no salt and no nonce. The salt's job — a unique, public, per-paste value that defeats cross-target precomputation — is already done perfectly by the 32-byte urlKey, which travels in the link exactly like a stored salt would. The nonce can be a fixed zero because AES-GCM only requires uniqueness per key, and every paste mints a fresh single-use key. Without a password there is no KDF at all: a KDF stretches low-entropy secrets, and a 256-bit CSPRNG key has nothing to stretch — so password-less pastes also open instantly. Together with a compact binary document replacing the v1 JSON wrapper, links are roughly 80–100 characters shorter.

Format v1 (legacy — still opened, never created)

Older links (#v1.<payload>.<key>) carried a 30-byte header with an explicit 16-byte PBKDF2 salt and 12-byte random nonce, derived every key as PBKDF2(urlKey ‖ password, salt, 600k), and wrapped the document in JSON. Every v1 link keeps decrypting forever; cross-implementation known-answer vectors for both formats are pinned in the test suite so neither can drift.

All primitives are the browser's native WebCrypto and Compression Streams - the cryptographic path imports zero third-party code.

Why this holds up

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; h4|Bin 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. h4|Bin is not an endpoint-security tool.
A malicious deployment serving altered code Mitigated, not eliminated: strict delivery headers reduce injection paths, but a compromised deployment could still serve altered application files. Users can inspect the delivered assets and Network panel, but web applications 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; h4|Bin refuses to make that promise.

Verify everything yourself

  1. Open browser DevTools, select the Network panel, and clear the existing request list.
  2. Type a distinctive sample paste and mint its encrypted link.
  3. Confirm that minting creates no network request and that the sample text never appears in a request.
  4. Inspect the address: the encrypted payload and key exist only after #, which browsers exclude from HTTP requests.
  5. Open the link in a private window and confirm that decryption happens locally without a data request.
  6. Change one character in the encrypted payload and confirm that AES-GCM authentication rejects the altered link.
  7. Create a password-protected paste, then confirm that a missing or incorrect password cannot decrypt it.
  8. Check delivery headers with curl -sI https://bin.h4rithd.com and look for CSP, HSTS, no-referrer, and nosniff.

Reporting a vulnerability

Found something? Contact the maintainer privately through h4rithd.com and do not publish details that could put users' pastes at risk. Reports are taken seriously and credited with permission.

format: v2 (v1 readable) · kdf: pbkdf2-sha256/600k · cipher: aes-256-gcm · last updated: 2026-07-10