1なぜつくったか

AIで資料を作る人が増えた。「これHTMLで出力して」と頼めば、レイアウト付きの資料、フロー図、インタラクティブな要素 — PDFやパワポにはない表現力を持つドキュメントが短時間で出来上がる。

でも、そうやって作ったリッチなHTML資料を「社外に送る」となった瞬間、困ることがある。

HTMLには、パスワード保護がない。

テキストエディタで開けば全文読める。メールで送った瞬間、中身は丸見え。PDFにもパワポにもZIPにもパスワード機能はあるのに、HTMLだけ — ない。

それなら作ろうと思った。

2つくったもの ― Secure HTML Container とは?

ZIPよりスマート、PDFより自由。かもしれない。

Secure HTML Container — どんなファイルもパスワード付きHTMLに変換するフォーマットだ。ファイルをドロップして、パスワードを決める。それだけで暗号化HTMLができあがる。サーバー不要。ブラウザだけで完結する。

暗号化されたファイルをブラウザで開くと、こんな画面になる:

ロック画面
このファイルはパスワードで保護されています
ファイル: 提案資料_v2.pdf
サイズ: 2.4 MB
作成者: 田中太郎
この情報は暗号化されていません
※ 心当たりのないファイルにはパスワードを入力しないでください
パスワード入力後
確認画面
復号が完了しました
ファイル名 提案資料_v2.pdf
サイズ 2.4 MB
作成者 田中太郎
メッセージ 先日の打ち合わせ資料です
※ ファイルの安全性は作成者の信頼性に依存します。
心当たりのない送信者からのファイルは開かないでください。

正しいパスワードを入力すると、確認画面が表示される。ファイル名・サイズ、そして作成者が設定していれば作成者名とメッセージが表示される。ここで「本当にこの人から送られたファイルか?」を確認してから、「ブラウザで開く」か「ダウンロード」を選べる。

動画・画像・音声・PDF・HTMLはブラウザ内でそのまま再生・表示できる。Excelなどブラウザで開けない形式はダウンロードされる。

Before → After

Before
HTML / PDF / Excel / 画像...
誰でも中身が読める
そのまま開けてしまう
After
暗号データ + 復号シェル
パスワード必須
単一 .html に変換

7つの特徴

  • どんなファイルでも対応 — HTML、PDF、画像、動画、Excel — ファイル形式を問わず暗号化HTMLに変換。メール添付で配布可能
  • 完全ブラウザ完結 — 暗号化も復号もブラウザの Web Crypto API だけ。Node.jsもサーバーも不要
  • 認証付き暗号 — AES-GCM により、暗号データの改ざんがあれば復号時に自動検知
  • オフライン動作 — 暗号化・復号どちらもサーバーへの通信は一切なし。データは外部に送信されない
  • 作成者情報の埋め込み — 暗号化時に作成者名とメッセージを暗号データ内に同封できる。受信者が「誰からのファイルか」を復号後に確認でき、フィッシング対策にもなる
  • テーマカラー — ロック画面のアクセントカラーをカスタマイズ可能。ブランドカラーで「いつもの色だ」と直感的に判別できる
  • インラインビューアー — 動画・画像・音声・PDF・HTMLはブラウザ内でそのまま再生・表示。ダウンロード前に中身を確認できる

3仕組み

暗号化ツール自体がHTMLファイル。ブラウザで開いて、ファイルをドロップしてパスワードを設定するだけ。

ファイルをバイナリ読み込み
ドロップされたファイルを ArrayBuffer として読み取る
メタデータ + ファイル本体を結合
ファイル名・MIMEタイプ・サイズ・作成者情報をJSON化し、ファイル本体と連結
AES-256-GCM で暗号化
salt + iv + ciphertext + authTag → Base64エンコード
※ v1.1でテキスト形式ファイルの自動圧縮に対応。対応形式や効果は暗号パラメータで後述
復号シェルHTMLを生成
ロック画面 + 確認画面 + 暗号データ + 復号スクリプトを1つのHTMLに組み立て。テーマカラーはCSS変数として直接埋め込み

出力されるHTMLの構造

<head> — フォント + ロック画面CSS
<body> — ロック画面UI(パスワード入力フォーム)
<script type="application/octet-stream"> — Base64エンコードされた暗号データ(メタデータ + ファイル本体)
<script> — Web Crypto API による復号スクリプト

ソースを覗いても見えるのはロック画面のHTMLと、意味のないBase64文字列だけ。元のファイルは暗号の中にある。

4暗号パラメータ

暗号パラメータの一覧
パラメータ理由
暗号アルゴリズムAES-256-GCM認証付き暗号。暗号化と改ざん検知を同時に行う
鍵導出PBKDF2-SHA256パスワード → 暗号鍵の変換。計算コストで総当たりを抑止
反復回数600,000OWASP推奨レンジ相当
Salt16バイト毎回ランダム。同じパスワードでも異なる鍵が生成
IV12バイトGCM標準のIV長(NIST SP 800-38D 推奨)
認証タグ16バイト改ざん検知用。暗号文末尾に連結

鍵導出の流れ

password (ユーザー入力)
    ↓
PBKDF2(password, salt, 600000, 'SHA-256')
    ↓
AES鍵 (256ビット = 32バイト)

データ形式

暗号データは . で連結した文字列。基本3パート + オプションの4パート目:

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

4パート目の preview_json は平文のJSONで、ファイル名・サイズ・作成者情報・テーマカラーを格納する。パスワード入力前にファイル情報を表示するために使われる。

プレビュー情報は暗号化されていない。 改ざん可能なため、信頼性の判断は復号後の確認画面(暗号データ内のメタ情報)で行うこと。ファイルの存在やサイズを秘匿したい場合は、暗号化時に「復号前にファイル情報を表示する」をOFFにすればよい。

テキスト自動圧縮(v1.1)

暗号データは Base64 でHTMLに埋め込むため、元ファイルより約33%大きくなる。v1.1 ではこの膨張を抑えるため、テキスト形式のファイルを暗号化前に自動圧縮する。

テキスト自動圧縮の条件
条件
対象MIMEtext/*application/jsonapplication/xmlimage/svg+xml
拡張子フォールバック.html .css .js .json .xml .svg .csv .txt .md .yml .php .sh .py .rb .java .c .cpp .ts .tsx .jsx .sql .log
最小サイズ10KB以上
アルゴリズムDeflate(CompressionStream API)
圧縮率テキスト系で 60—80% 削減

圧縮の有無は preview_jsoncompressed フラグで管理される。復号側はフラグを見て DecompressionStream で展開する。バイナリファイル(PDF・画像・PPTX等)は圧縮対象外のため、従来と同じ動作になる。

ブラウザ互換性: CompressionStream / DecompressionStream は Chrome 80+、Safari 16.4+、Firefox 113+ で対応。SHCの主なターゲットはHTML資料のカジュアルな暗号化であり、これらのブラウザカバー率で実用上の問題はない。非対応ブラウザでは圧縮がスキップされ、従来どおり非圧縮で暗号化される。
この方式で防げないもの: パスワード自体の漏洩、スクリーンキャプチャ、DevToolsでの確認、ブラウザのゼロデイ。これらは運用でカバーする領域。
でも、それはPDFのパスワード保護やZIP暗号化も同じだ。「絶対に破れない」のではなく「うっかり転送しても中身が読まれない」「ファイルを拾っただけでは内容がわからない」— このレベルの保護が、実務では一番求められている。

5ブラウザ側の復号

復号はブラウザの Web Crypto API だけで完結する。外部ライブラリの読み込みは一切不要。

プレビュー表示 — ペイロードが4パートの場合、パスワード入力前にファイル名・サイズ・作成者情報を表示(平文JSONを読むだけ)
暗号データ取得<script type="application/octet-stream"> を読む
分割. で split → salt / iv / data(+ preview_json)に分離
鍵マテリアル変換crypto.subtle.importKey
鍵導出crypto.subtle.deriveKey で PBKDF2 → AES-256 鍵
復号 + 認証crypto.subtle.decrypt + authTag検証
確認画面 — 復号成功後、ファイル名・サイズ・作成者情報を表示。ここで表示されるメタデータはAES-GCMの認証タグで保護されており、改ざん不可
ファイル配信 — 「ブラウザで開く」でインラインビューアー起動(動画・画像・音声・PDF・HTML対応)、または「ダウンロード」でファイル保存

パスワード誤り時

AES-GCM は鍵が異なると認証タグの検証で必ず失敗し、例外をスローする。「部分的に復号された壊れたデータ」が表示されることはない。全か無か。

総当たり対策

  • PBKDF2 60万回 — 1回のパスワード試行に数百ミリ秒。大量試行が非現実的
  • 試行回数制限 — 最大10回。超過でフォームロック
  • 指数バックオフ — 3回目の失敗から 2秒 → 4秒 → 8秒 → ... → 最大30秒

6使い方とFAQ

ツール自体が1枚のHTMLファイル。ブラウザで開くだけで使える。

ツールをブラウザで開く
HTMLファイルをダブルクリック。インストール不要
保護したいファイルをドラッグ&ドロップ
HTML、PDF、画像、動画、Excel — 50MB程度まで対応
パスワードを設定
詳細設定(任意)
作成者名・メッセージ・テーマカラー・プレビュー表示を設定できる。設定しなくても暗号化は可能
「パスワードで保護する」をクリック
ボタンがプログレスバーに変化し、完了するとダウンロードボタンに切り替わる。タップしてファイルを保存
データは外部に送信されない。 暗号化処理はすべてブラウザ内で完結する。ファイルがサーバーに送られることは一切ない。

なぜ外部ライブラリを使わないのか?

CryptoJS 等を使えば実装は楽になる。だがCDN障害時に復号不能になるリスクがある。ブラウザの Web Crypto API はOS/ブラウザのネイティブ暗号実装を使うため、信頼性が高い。

なぜ Argon2 ではなく PBKDF2 か?

Argon2id はメモリハード関数で PBKDF2 より優れた鍵導出関数だ。だが Web Crypto API が Argon2 をサポートしていない。ブラウザ側で外部ライブラリなしに実装するには PBKDF2 が唯一の選択肢。反復回数 60万回で十分な総当たり耐性を確保している。

同じパスワードで2回暗号化すると同じ暗号文になる?

ならない。Salt と IV は毎回ランダム生成されるため、同じパスワード・同じファイルでも毎回異なる暗号文になる。

ファイルサイズが増えるのでは?

Base64埋め込みにより約33%増える。ただしv1.1でテキスト形式ファイルの自動圧縮に対応した。HTML資料やソースコードなら圧縮で60—80%削減されるため、Base64の膨張を差し引いても元ファイルより小さくなる。画像やPDFなど内部圧縮済みのファイルは圧縮対象外で、従来どおり約33%増となる。

7おわりに

必要な道具はすべて、すでにブラウザの中にあった。

Web Crypto API、AES-256-GCM、PBKDF2 — どれも全モダンブラウザに搭載済みの標準技術だ。サーバーも外部ライブラリも要らない。HTMLファイル1枚でツールが完結する。

AIでHTML資料を作る人が増えた今、「パスワードをかけて渡したい」は当然のニーズだ。Secure HTML Container はそのためのフォーマットだ。

HTMLにパスワードをかけたかった。方法がなかったから、作った。それだけの話だ🥴