ワンタイムリンクを一度開いたあと、サーバーに残るのは何か

運用担当がデータベースのパスワードをチャットへ投げ、同僚が復旧コードをチケットへ貼る。あとから来る問いは、アルゴリズムの名前ではなく、「相手が開いたあと、その文字列はどの機械に残っているか」です。一度開くと消えるリンクが答えるのは後半です。サーバーに平文が載ったことはありません。暗号文は、取り出しに成功し、設定回数を使い切ったあとに消えます。URL の文字列はチャット履歴に残り得ます。それは別の層のリスクです。

照合できる結論を先に一つ。閲覧ページを最初に開いたときは、殻のページだけを読み、まだサーバーへ暗号文を取りに行かない。人が「開いて見る」を押したあと、Network に番号付きの取り出しが出る。成功して回数を使い切ったあと、同じ番号への次の要求は 410 になる。アドレスバーには # の後ろの鍵があってよい。同じ列がリクエスト行に出てはいけない。

「一度開くと消える」と言うと、青い文字そのものが燃える絵を思い浮かべがちです。比喩は対象を取り違えます。チャットの吹き出しは、行き先が 410 を返したからといって消えません。ブラウザの履歴、メール本文、チケットのコメントも残ります。消えるのは、ホスト上の短い寿命の暗号文です。手元の完全な URL は、依然として一列の文字です。もう一度開いても、番号が指す対象はもうありません。

「何が残るか」を聞く前に、二つの証憑を分けてください。一つは位置指定です。クエスチョンの後ろの番号で、サーバーにどの記録を取るかを伝えます。もう一つは復号鍵です。ハッシュの後ろにあり、このタブに残ります。ホストは設計上、前者を見て、後者を見ません。一度の成功した閲覧のあと、位置指定は空の場所を指し得ます。鍵がチャット履歴に残っていても、暗号文が無いので何も復号できません。

共有フォルダのリンクは逆です。リンクが有効なあいだ、ファイルはだいたいディスクに残っています。取り消すのは権限であり、最初に開いた瞬間に対象を消すわけではありません。ワンタイムリンクは、「読む」と「消す」を同じ取り出しに結びます。作成時に閲覧回数(1–10、既定は 1)と有効期限(1 時間、24 時間、7 日、または読んだあとだけ消滅)を選べます。既定の道は、最初の成功した取り出しで暗号文を削除することです。未読のまま期限切れでも、同じ削除です。どちらにも、あとから取り戻せる平文の控えはありません。

サーバーに平文が載ったことはない

「一度読んだら平文を消す」も、よくある取り違えです。順序はこうです。平文は送り手のタブにだけあります。ブラウザは Web Crypto で 256 ビットの乱数鍵を引き、AES-256-GCM で暗号化します。出域の欄は暗号文、有効期限、最大閲覧回数です。サーバーは推測しにくい番号を返します。ページはその鍵を s.html?id={番号}#{鍵} のフラグメントへつなぎます。「先にパスワードをデータベースへ書いて、あとから暗号化する」段階はありません。

パラメータは標語ではなく、その場で照合できます。NIST SP 800-38D は、GCM の IV に 96 ビット(12 バイト)を勧めています。相互運用と実装の単純さのためです。認証タグはよく 128 ビット(16 バイト)です。Web Crypto の AesGcmParams もこの形です。MakePwd の暗号文は、12 バイトの IV + 暗号文 + 16 バイトのタグを Base64 にしたものです。鍵は 32 バイト。一件の上限は 32 KB です。数字は作成ページに書いてあります。いま作ったリンクでも照合できます。ハッシュの後ろは、いま打った文ではなく、鍵の Base64URL です。

GCM は完全性も与えます。暗号文やタグが改ざんされていれば、このタブでの復号は失敗します。「だいたい近い」平文は出ません。途中の改ざんは防げます。相手が読んだあとのスクリーンショットは防げません。サーバーは最初から鍵を持たないので、代わりに復号できず、本文を検査できず、読んだあとに平文の控えを作れません。できるのは、番号で暗号文を渡し、回数または TTL でその暗号文を消すことだけです。

段階 ブラウザにあるもの サーバーにあるもの
作成が終わる いま打った平文と、完全なリンク 暗号文、番号、TTL、残回数
閲覧ページが最初に描画される アドレスバーの番号と # の鍵 まだ暗号文。この描画では消えない
確認して取り出しが成功する このタブで復号した平文 回数を使い切れば削除。残っていれば回数を一つ減らす
同じ番号をもう一度開く 鍵はアドレスバーに残り得る 消滅済み、または期限切れ。410 系の応答

番号はクエスチョン、鍵はハッシュ

一度きりのリンクは、同時に二つの仕事をします。サーバーにどの記録を取るかを伝え、受け手のブラウザにどの鍵を使うかを伝える。この二つを、同じ HTTP の欄に詰め込んではいけません。クエスチョンの後ろのクエリはリクエスト行に書かれ、リバースプロキシとアクセスログは設計上それを見ます。ハッシュの後ろのフラグメントは、仕様上クライアントが処理します。

RFC 9110 の 7.1 節ははっきり書いています。対象 URI にフラグメントは含まない。フラグメント識別子はクライアントが処理するからです。ブラウザがサーバーに求めるのは s.html?id=… であり、アドレスバー全体ではありません。鍵を ?key= へ移した瞬間、「暗号文だけを持つ」はログの層ですでに成立しません。その日のアクセスログを開けば、復号に必要な後半が見えます。

この層は前の記事で展開しています。ここでの境界は短いです。分けるのは、ホストに鍵を見せないためです。完全な列を気軽に転送してよい、という意味ではありません。# 付きの全体をチャットへ貼れば、受け手もチャット事業者も鍵を見ます。フラグメントが防ぐのは HTTP です。クリップボードは防ぎません。Network で照合できる手順は、URLのハッシュ記号の後ろに鍵を置ける理由と、その保護が効かなくなるときにあります。

消滅するのは暗号文を取ったその一回

閲覧ページを開くことと、閲覧回数を一つ使うことは、同じ出来事ではありません。閲覧ページの HTML は先に描けます。アドレスに ?id=# の鍵があるかを見て、確認ボタンで止まる。この段階が消費するのは静的リソースだけです。サーバーに暗号文の削除を伝えるのは、そのあとの番号付き取り出しです。ボタンを押し、取り出しが成功し、回数を使い切ったとき、記録は「まだ取れる」から「消滅した」へ移ります。

鍵が無いときは、取りに行ってはいけません。アドレスに番号だけがあり、チャットアプリがハッシュを落としたなら、閲覧ページはフラグメントが欠けていると示すべきです。先にサーバーへ暗号文を求めない。そうしないと、唯一の回数を無駄にします。サーバーは暗号文を渡して消し、ブラウザは復号できず、送り手は相手が読んだと思い込みます。照合できる振る舞いはこうです。# が無いとき、Network に暗号文のインターフェースは出ない。完全なリンクがあり、確認を押したとき、初めて出る。

回数は常に 1 ではありません。作成時は 1 から 10 を選べます。3 にすると、最初の二回の成功した取り出しのあと、サーバー上の暗号文はまだあります。減るのは残回数だけです。三回目の成功で消えます。既定の 1 は、「パスワードを一度渡す」という仕事に合わせたものであり、プロトコルが一度しか読めないからではありません。TTL は別の削除条件です。24 時間未読なら暗号文は同じく消え、状態は「誰かがすでに読んだ」と区別できるべきです。MakePwd の閲覧ページは、410 応答の expired を期限切れとして扱い、ほかの消滅状態はすでに消えたものとして扱います。

本物のパスワード、本番の API キー、マスクしていない接続文字列で実験しないでください。捨てられるカナリア、たとえば canary-burn-2026-do-not-reuse を使います。照合するのはステータスコードとリクエスト行であり、秘密をもう一度広げることではありません。

410 と 404:消えた、期限切れ、もともと無い

暗号文が消えたあとでも、サーバーは次の要求に答えます。RFC 9110 の 15.5.11 節410 Gone をこう書いています。対象リソースはオリジンサーバー上でもう使えず、その状態はおそらく永久である。オリジンが永久かどうかを判断できないなら、404 を使うべきです。MDN の 410 の説明は、クライアントは繰り返し再試行すべきではなく、サイトはまだそのリソースを指すリンクを外すべきだと補います。

一度きりの秘密では、410 の方が 404 より近いです。この番号はかつて暗号文の対象を指していた。意図して消した。戻ってこない。期限切れも回数切れも 410 にしてよく、本文で expired と通常の消滅を分ければ、受け手は「リンクを打ち間違えた」と思いません。404 も出ます。番号の形式が無効なとき、記録が「かつて存在した」とすら言わないところまで消されたときです。送り手にとって、どちらも同じ意味です。ホストから中身を取り戻せると思わない。

410 には、クローラへ「もう来ないでよい」と伝える層もあります。閲覧ページは一時的な暗号文の着地です。検索エンジンには noindex とし、sitemap にも入れません。一度きりのパスワードのページが索引に載ってほしくはありません。作成ページは索引に入れてよい。リンクの作り方を説明するからです。閲覧の着地は、完全な URL をすでに持っている人だけに仕えます。

チャットのプレビューが先に回数を使う理由

よくある事故は、暗号学者が AES を破ることではありません。プレビュー用のボットが、同僚のクリックより早いことです。完全な URL をチャットへ貼ると、カードを描くためにクライアントがその住所へ要求を出します。Slack のリンク展開の文書は率直です。既定では、メッセージにリンクが出ると、Slack はそれを取得してプレビューを出します。LINE のサムネイル、Teams、Discord、一部のメールセキュリティ検査も、同類の取得をします。欲しいのは題と要約であり、パスワードではありません。しかし「最初の GET で暗号文を取り、消す」なら、その取得が唯一の回数を使います。同僚が開くのは、すでに消滅したページです。

# の後ろの鍵は、このサーバー側取得に対する一層です。ボットが取るのは s.html?id=… であり、フラグメントは HTTP に付きません。プレビューは通常復号できないので、カードにパスフレーズは出ません。焼けるのは閲覧回数であり、鍵そのものではありません。閲覧ページが読み込みと同時に暗号文を取るなら、プレビューは受け手の代わりに「開いて見る」を押したのと同じです。相手は 410 を見る。送り手は、すでに読んだと思い込む。

実行できる対策は、「リンクを貼るな」ではありません。「ページを描く」と「暗号文を取る」を分けることです。閲覧ページは先に確認画面を描き、このクリックが回数を使うと伝え、人が動かしてから取り出しを送ります。HTML だけを取り、ボタンを押さないプレビューは、確認で止まります。スクリプトを最後まで走らせ、クリックを合成する検査は止められません。人が間違ったカードを押すことも止められません。よくある「カードが先に一度開いた」失敗を、既定の結末から、リンクが残る側へ移します。

自動で展開するチャンネルでは、もう一つの習慣が効きます。「パスワードはワンタイムリンクで送る。URL は別メッセージ」と先に書くか、プレビューしない個人チャットへ完全な列を置く。完全な URL は、持っている人が使える証憑のままです。プレビューが使うのは回数だけです。「列の全体を持った人が復号できる」という層は変わりません。

その場で確かめる:確認画面で止め、Network を見る

「一度開くと消える」「ゼロ知識」は、標語だけでは証明になりません。その場で見えるのは四つです。作成時に平文が出たか。閲覧ページの最初の読み込みで暗号文を取ったか。確認のリクエスト行に鍵が入ったか。回数を使い切ったあとの二度目の開きが 410 か。

先にカナリアを用意します。ワンタイムリンクの作成ページを開き、捨てられる一文を打ち、TTL を 1 時間、閲覧回数は 1 のままにします。結果は誰にも送らない。結果欄の分割を見ます。クエリ側は番号だけ、フラグメント側が鍵です。開発者ツールの Network を開き、Preserve log を入れ、テスト文の全文で検索します。作成要求の本文は暗号文であり、その原文は出てはいけない。分析送信にも原文は出てはいけない。

  1. 同じ完全なリンクで閲覧ページを開く。まだ確認は押さない。Network に出るのはドキュメントと静的リソースだけであり、番号付きの暗号文取り出しは出ない。
  2. アドレスバーと、ドキュメント要求のリクエスト行を並べる。アドレスバーは # とその後ろを残す。リクエスト行に見えるのは path と ?id= だけ。
  3. 確認を押す。このとき初めて暗号文の取り出しが出る。成功したあと、ページは平文を出す。同じリンクをもう一度開く。すでに消滅した状態、または 410 になり、同じカナリアは二度と出ない。

三つとも揃っても、支えられる結論は狭いです。このブラウザ、この一回の操作では、平文は観測できた業務欄として出ておらず、鍵はリクエスト行に入っておらず、一度の成功した取り出しのあと暗号文はもう取れない。拡張機能が入力欄を読んでいないことの証明にはなりません。ディスクが破られたあとに、過去の暗号文が絶対に無いことの証明にもなりません。ブラウザを変えたら、カナリアをもう一度走らせる価値があります。任意のツールページで平文が端末を出たかを見るなら、手順はブラウザで暗号化するなら、平文が端末を出ていないことをその場で確かめるにあります。

コピー、スクリーンショット、完全なリンクの転送は止められない

一度開くと消える設計が減らすのは、二つのリスクです。サーバーが平文を長く持つこと。同じ暗号文が何度も開かれること。減らさない三つ目があります。受け手が平文を見たあと、何をするかです。コピー、スクリーンショット、転送、隣の人への読み上げはできます。リンクそのものも、完全なまま転送できます。鍵と番号が一緒に動き、回数が残っていれば、次の人も閲覧ページで復号できます。

「相手はログインしないと開けない」が受け渡しを解決しないのも、同じ理由です。アクセス制御は完全な URL です。閲覧ページにアカウントの扉を付けても、鍵がサーバーへ行かなくなるわけではありません。鍵はもともとサーバーへ送るべきではない。増えるのは受け手の扉だけであり、「誰がリンクを持っているか」が「誰がアカウントを持っているか」に変わるだけです。MakePwd にアカウントとパスワード保管庫はありません。作成ページも閲覧ページも、開いてすぐ使えます。閲覧ページは受け手に公開です。

容量にも境界があります。32 KB は、パスワード、API キー、復旧コード、短い説明には足ります。データベースのダンプや、証明書一式には足りません。ファイル一式はこの端末で .lock / .enc にし、共有フォルダかメールで運びます。ファイル暗号化は一件 5 GB まで、同じく開いてすぐ使え、平文は既定ではアップロードしません。大きなファイルをワンタイムリンクへ押し込むのは、「より安全」ではありません。この短いテキストの通路の設計の外です。

開いてすぐ使える作成ページで、消滅した状態を練習する

結果欄にクエリとフラグメントの分割を書く道具で練習したいなら、MakePwd のワンタイムリンクから始めてよい。どちら側も登録は不要です。作成時の暗号化はこのタブで起き、アルゴリズムは AES-256-GCM です。サーバーが受け取るのは暗号文、ttl_hours(0–168、既定 24)、max_reads(1–10、既定 1)です。生成されるリンクの形は常に s.html?id={番号}#{鍵} です。閲覧ページは確認で止まり、それから暗号文を取り、このタブで復号します。

練習では上のカナリアを使い、閲覧回数は 1 のままにします。同時に三箇所を見ます。作成結果のクエリ / フラグメント分割。閲覧ページの最初の読み込みで、Network が先に暗号文を取っていないか。二度目の開きが消滅しているか。分割は、鍵がクエスチョンに入っていないことを確認します。最初の読み込みは、プレビューが誤って消さないことを確認します。二度目の開きは、「一度開くと消える」が文面だけではないことを確認します。

中身を出す前に追跡パラメータを剥がす必要があるなら、先に UTM削除・マスキングを通し、本当に秘匿したい短い文だけをワンタイムリンクへ入れます。洗浄が解くのは、クエリの UTM とクリック ID です。一度きりの暗号文の通路の代わりにはなりません。どの段階もログインを求めず、未接続のサポートメールも出しません。

よくある質問

一度開いたあと、サーバーに平文は残りますか?

平文は最初から載っていません。作成時にブラウザが先に暗号化し、サーバーが受け取るのは暗号文だけです。取り出しに成功し、設定回数に達したあと、暗号文は削除されます。同じ番号への次の要求は 410、またはすでに消滅した状態になります。URL の文字列はチャット履歴に残り得ます。それはホスト上の平文の控えではありません。

チャットのプレビューが先に中身を消しますか?

回数は使い得ます。平文は通常消えません。プレビューが取るのはページであり、# の後ろの鍵は HTTP に入りません。閲覧ページが読み込みと同時に暗号文を取るなら、プレビューが唯一の回数を使います。正しい実装は、先に確認し、それから取りに行きます。Slack などは、カードを描くためにメッセージ内のリンクを既定で取得します。LINE のサムネイルも同じ層です。

410 と 404 の違いは何ですか?

410 は、そのリソースはかつて使えて、いまは永久に外されたという意味です。クライアントは再試行すべきではありません。404 はより曖昧で、番号が無効なことも、記録がすでに消えていることもあります。ワンタイムリンクでは、410 で消滅と期限切れを分ける方がはっきりします。どちらでも、復元できる平文はありません。

閲覧ページを開くのに、ログインは必要ですか?

不要です。作成も閲覧も、開いてすぐ使えます。受け手は番号で暗号文を取り、ハッシュの鍵でこのタブで復号します。MakePwd にアカウントとパスワード保管庫はありません。アクセス制御は、完全なリンクそのものです。

次に一度きりの秘密を送るとき、覚えておく三つのこと

第一に、「一度開いたあと何が残るか」を聞くなら、先にサーバーに平文が載ったことがあるかを聞く。載ったことはない。消えるのは暗号文です。チャットに残った URL は、何も復号できません。第二に、消滅点は暗号文の取り出しであり、閲覧ページの最初の描画ではありません。確認ボタンが止めるのはプレビュー用のボットであり、すでに列の全体を持っている人ではありません。第三に、照合は Network でする。作成時はカナリア原文を探す。閲覧時はリクエスト行と # を並べる。二度目の開きは 410 を見る。

鍵をハッシュ記号の後ろに置ける理由をまだ追うなら、URLのハッシュ記号の後ろに鍵を置ける理由と、その保護が効かなくなるときを読んでください。本稿は、「一度開いたあと、サーバーに残るのは何か」を、ステータスコードと要求のタイミングに照合できる範囲へ落とすだけです。