1Why We Built This

More and more people are using AI to create documents. Ask it to "output this as HTML," and you get richly formatted documents with layouts, flow diagrams, interactive elements — things PDF and PowerPoint simply can't do. All in a fraction of the time.

But the moment you try to send one of those rich HTML documents externally, you run into a problem.

HTML has no password protection.

Open it in a text editor and the entire content is readable. Send it via email and everything is exposed. PDF has it. PowerPoint has it. ZIP has it. But HTML — nothing.

So we decided to build one.

2What We Built — Secure HTML Container

Smarter than ZIP, freer than PDF. Maybe.

Secure HTML Container — a format that turns any file into a password-protected HTML. Drop a file, set a password. That's it — you get an encrypted HTML file. No server required. Everything runs in the browser.

When you open an encrypted file in a browser, this is what you see:

Lock Screen
This file is password protected
File: proposal_v2.pdf
Size: 2.4 MB
Author: John Smith
This information is not encrypted
* Do not enter a password for files you don't recognize
After entering password
Confirmation Screen
Decryption complete
File proposal_v2.pdf
Size 2.4 MB
Author John Smith
Message Meeting materials from last week
* File safety depends on the trustworthiness of the author.
Do not open files from unknown senders.

Enter the correct password and a confirmation screen appears. It shows the file name, size, and — if the author set them — their name and a message. Here you can verify whether the file is really from the person you expect, then choose to "Open in browser" or "Download."

Videos, images, audio, PDFs, and HTML can be viewed or played directly in the browser. Formats that browsers can't open natively (like Excel) are downloaded instead.

Before → After

Before
HTML / PDF / Excel / Images...
Anyone can read the contents
Can be opened as-is
After
Encrypted data + decryption shell
Password required
Converted to a single .html file

7 Key Features

  • Works with any file type — HTML, PDF, images, video, Excel — any file format can be converted to an encrypted HTML. Distributable as an email attachment
  • Fully browser-based — Both encryption and decryption use only the browser's Web Crypto API. No Node.js, no server required
  • Authenticated encryption — AES-GCM automatically detects any tampering with the encrypted data during decryption
  • Works offline — Both encryption and decryption require zero network communication. Your data never leaves the browser
  • Embedded author info — Author name and message can be included inside the encrypted data. Recipients can verify who sent the file after decryption, providing anti-phishing protection
  • Theme color — Customize the accent color of the lock screen. Use your brand color so recipients instantly recognize it as "the usual color"
  • Inline viewer — Videos, images, audio, PDFs, and HTML can be played or displayed directly in the browser. Preview contents before downloading

3How It Works

The encryption tool itself is an HTML file. Open it in a browser, drop a file, set a password. That's all.

Read the file as binary
The dropped file is read as an ArrayBuffer
Combine metadata + file body
File name, MIME type, size, and author info are serialized to JSON and concatenated with the file body
Encrypt with AES-256-GCM
salt + iv + ciphertext + authTag → Base64-encoded
* v1.1 added automatic compression for text-based files. See Crypto Parameters for supported formats and details
Generate the decryption shell HTML
Lock screen + confirmation screen + encrypted data + decryption script are assembled into a single HTML. Theme color is embedded directly as a CSS variable

Structure of the Output HTML

<head> — Fonts + lock screen CSS
<body> — Lock screen UI (password input form)
<script type="application/octet-stream"> — Base64-encoded encrypted data (metadata + file body)
<script> — Decryption script using Web Crypto API

Even if you view the source, all you'll see is the lock screen HTML and a meaningless Base64 string. The original file is locked inside the cipher.

4Crypto Parameters

List of cryptographic parameters
ParameterValueRationale
Encryption algorithmAES-256-GCMAuthenticated encryption. Encrypts and detects tampering simultaneously
Key derivationPBKDF2-SHA256Converts password → encryption key. Computational cost deters brute force
Iterations600,000Within the OWASP-recommended range
Salt16 bytesRandom each time. Same password produces a different key every time
IV12 bytesStandard GCM IV length (NIST SP 800-38D recommendation)
Auth tag16 bytesFor tamper detection. Appended to the ciphertext

Key Derivation Flow

password (user input)
    ↓
PBKDF2(password, salt, 600000, 'SHA-256')
    ↓
AES key (256 bits = 32 bytes)

Data Format

The encrypted data is a .-delimited string. Three base parts + an optional fourth:

base64(salt) . base64(iv) . base64(ciphertext + authTag) [. base64(preview_json)]

The fourth part, preview_json, is plaintext JSON containing file name, size, author info, and theme color. It's used to display file info before the password is entered.

Preview info is not encrypted. Since it can be tampered with, trustworthiness should be determined from the confirmation screen after decryption (which uses metadata from inside the encrypted data). If you want to hide the file's existence or size, turn off "Show file info before decryption" during encryption.

Automatic Text Compression (v1.1)

Because encrypted data is embedded as Base64 in HTML, it's about 33% larger than the original file. v1.1 introduced automatic compression for text-based files before encryption to reduce this bloat.

Automatic text compression conditions
ConditionValue
Target MIME typestext/*, application/json, application/xml, image/svg+xml
Extension fallback.html .css .js .json .xml .svg .csv .txt .md .yml .php .sh .py .rb .java .c .cpp .ts .tsx .jsx .sql .log etc.
Minimum size10 KB or larger
AlgorithmDeflate (CompressionStream API)
Compression ratio60—80% reduction for text-based files

Whether compression was applied is tracked by the compressed flag in preview_json. The decryption side checks this flag and decompresses using DecompressionStream. Binary files (PDFs, images, PPTX, etc.) are not compression targets and behave the same as before.

Browser compatibility: CompressionStream / DecompressionStream are supported in Chrome 80+, Safari 16.4+, and Firefox 113+. SHC's primary target is casual encryption of HTML documents, and these browsers provide sufficient coverage. On unsupported browsers, compression is simply skipped and files are encrypted uncompressed, just like before.
What this approach cannot protect against: Password leakage, screen capture, DevTools inspection, browser zero-days. These are threats that must be addressed through operational practices.
But the same is true for PDF password protection and ZIP encryption. It's not about being "absolutely unbreakable" — it's about ensuring that an accidentally forwarded file can't be read, or that a file someone stumbles upon reveals nothing. That level of protection is what's most needed in practice.

5Browser-Side Decryption

Decryption runs entirely on the browser's Web Crypto API. No external libraries are loaded at all.

Preview display — If the payload has 4 parts, file name, size, and author info are shown before password entry (just reading the plaintext JSON)
Fetch encrypted data — Read the <script type="application/octet-stream"> element
Split — Split by . → separate into salt / iv / data (+ preview_json)
Import key materialcrypto.subtle.importKey
Derive keycrypto.subtle.deriveKey for PBKDF2 → AES-256 key
Decrypt + authenticatecrypto.subtle.decrypt + authTag verification
Confirmation screen — After successful decryption, file name, size, and author info are displayed. The metadata shown here is protected by the AES-GCM authentication tag, making it tamper-proof
File delivery — "Open in browser" launches the inline viewer (supports video, images, audio, PDF, HTML), or "Download" saves the file

Wrong Password

AES-GCM always fails auth tag verification when the key is wrong, throwing an exception. There's no risk of "partially decrypted corrupted data" being displayed. It's all or nothing.

Brute Force Protection

  • 600,000 PBKDF2 iterations — Each password attempt takes hundreds of milliseconds. Mass attempts become impractical
  • Attempt limit — Maximum 10 tries. After that, the form locks
  • Exponential backoff — Starting from the 3rd failure: 2s → 4s → 8s → ... → max 30s

6Usage and FAQ

The tool itself is a single HTML file. Just open it in a browser.

Open the tool in a browser
Double-click the HTML file. No installation needed
Drag & drop the file you want to protect
HTML, PDF, images, video, Excel — supports files up to about 50 MB
Set a password
Advanced settings (optional)
You can set author name, message, theme color, and preview display. Encryption works without these settings too
Click "Protect with password"
The button transforms into a progress bar, then switches to a download button when complete. Tap it to save the file
Your data never leaves the browser. All encryption processing runs entirely within the browser. Files are never sent to any server.

Why no external libraries?

Using something like CryptoJS would make implementation easier. But if the CDN goes down, decryption becomes impossible. The browser's Web Crypto API uses the OS/browser's native crypto implementation, making it far more reliable.

Why PBKDF2 instead of Argon2?

Argon2id is a memory-hard function and a superior key derivation function compared to PBKDF2. But Web Crypto API doesn't support Argon2. To implement it in the browser without external libraries, PBKDF2 is the only option. With 600,000 iterations, it provides sufficient brute-force resistance.

Does encrypting with the same password twice produce the same ciphertext?

No. Salt and IV are randomly generated each time, so even the same password and file produce different ciphertext every time.

Doesn't the file size increase?

Base64 embedding adds about 33%. However, v1.1 introduced automatic compression for text-based files. For HTML documents and source code, compression reduces size by 60—80%, so even after Base64 overhead, the result can be smaller than the original. For images, PDFs, and other internally compressed formats, compression is skipped and the 33% increase applies as before.

7Closing Thoughts

Everything we needed was already inside the browser.

Web Crypto API, AES-256-GCM, PBKDF2 — all standard technologies built into every modern browser. No server, no external libraries. The entire tool fits in a single HTML file.

Now that more people are creating HTML documents with AI, "I want to send this with a password" is a natural need. Secure HTML Container is the format for that.

We wanted to password-protect HTML. There was no way to do it. So we built one. That's the whole story.🥴