招待URL、共有リンク、パスワード再設定リンク、ワンタイムURLなど、「そのURLを知っている人だけがアクセスできる」仕組みを作る場面は、実務で数多くあります。
しかし、仕組みを知らずに実装すると、「推測されてしまうURL」や「いつまでも使い続けられるURL」になりがちで、情報漏洩や不正アクセスの原因になります。本記事では、安全なリンクを発行する代表的な方式を整理し、実際にトークンがどう生成され、改ざんがどう検知されるのかを体験しながら理解します。
リンクの運用・共有方法そのものを見直したい場合は、 日程調整リンクのセキュリティで見落とされがちな3つの視点 もあわせてご覧ください。本記事は、その裏側でリンクをどう技術的に発行するかという実装寄りの内容です。
「安全なURLを発行する」といっても、方式はいくつかあります。実務でよく登場するのは、次の7つです。
| 名称 | 仕組み | 主な用途 |
|---|---|---|
| HMAC署名 | 秘密鍵とデータから署名値を生成する | 改ざん防止リンク、署名付きURL |
| 署名付きURL(Signed URL) | URLのパラメータにHMAC等の署名を付与する | 一時リンク、認証リンク |
| トークン方式(ランダムトークン) | ランダムなユニーク文字列を生成しDBに保存する | 招待URL、ワンタイムURL |
| JWT | データを持ったトークンを秘密鍵で署名する | ログイン、API認証 |
| UUID | 規則性のない一意なIDを生成する | 単なる識別子 |
| ハッシュ | 入力データから固定長の文字列を生成する | 照合、改ざん検知 |
| 暗号化トークン | データそのものを鍵で暗号化する | URL内のデータを隠す |
名前だけ見ると似ていますが、それぞれ役割が異なります。特に混同されやすいのが、「ランダムトークン」と「HMAC署名(署名付きURL)」の2つです。この後、実際に体験しながら違いを確認します。
まず前提として、なぜランダム性が重要なのかを整理します。
例えば、共有リンクをhttps://example.com/share/1001のような連番IDで発行しているとします。この場合、URLの数字を1つ変えるだけで、他人に発行されたはずの1002や1003のページにもアクセスできてしまう可能性があります。
これは、Webアプリの脆弱性としてよく知られる「アクセス制御の不備」の典型例です。IDが連番であること自体が、攻撃者にとっての手がかりになってしまいます。
言葉で説明するより、実際に発行してみたほうが違いがはっきりします。両方の「発行する」ボタンを、何度か押してみてください。
両方の「発行する」ボタンを、それぞれ何度か押してみてください。実際の通信は発生せず、あなたのブラウザの中だけでURLを生成します。
次にどんなURLが発行されるか、見ただけで分かってしまいます。
同じ形式でも、次にどんな文字列になるかは推測できません。
ランダムトークンは、このブラウザが標準で備えている暗号学的に安全な乱数生成器(Web Crypto API)を実際に使って、その場で生成しています。演出用の見た目だけの値ではありません。
連番ID側は、押すたびに1ずつ増えるだけなので、次にどんなURLが発行されるか誰でも予測できます。一方のランダムトークン側は、同じ長さの文字列でも、次に何が出るかはまったく予測できません。これは演出ではなく、実際にブラウザの暗号学的な乱数生成器(Web Crypto API)を使って生成しているためです。
ランダムトークンは「推測されない」ことが強みですが、それだけでは「値を書き換えられていないか」までは分かりません。そこで使われるのが、HMAC署名付きURLです。
仕組みはシンプルです。user_idやexpires(有効期限)といったデータと、サーバーだけが知っているsecret_keyから署名値を計算し、URLに付け加えます。サーバーは受け取った際に同じ計算をやり直し、URLに付いていた署名と一致するかどうかだけを確認します。
「署名付きURLを発行する」を押すと、この記事のサンプルデータから実際にHMAC-SHA256の署名を計算します。そのあと、値を書き換えて検証してみてください。
発行されたURL
サーバー側は受け取ったuser_id・expiresと、自分だけが知っているsecret_keyから署名を再計算し、URLに付いていた署名と一致するかを確認しているだけです。secret_keyを知らない第三者は、正しい署名を作れないため、値を書き換えると検知されます。
「署名付きURLを発行する」を押すと、実際にHMAC-SHA256の署名がこの記事のサンプルデータから計算されます。その後、user_idの欄を書き換えてから「検証する」を押すと、署名が一致せず「改ざんが検知されました」と表示されたはずです。書き換えなければ、署名は一致します。secret_keyを知らない限り、書き換えた値に対応する正しい署名を作ることはできません。
ここまでの2つ(ランダムトークン・HMAC署名)を軸に、他の方式との使い分けを整理します。
| やりたいこと | 適した方式 |
|---|---|
| 単なる識別子として、重複しなければよい | UUID |
| そのURLを知っている人だけがアクセスできればよい | ランダムトークン(CSPRNG)+DB保存 |
| URLの値が書き換えられていないことを確認したい | HMAC署名(署名付きURL) |
| ログイン状態やAPI認証の情報を持ち運びたい | JWT |
| URL自体にデータを隠したい | 暗号化トークン |
JWTは認証・API連携の文脈では非常によく使われますが、「共有リンクを1本発行したいだけ」という用途にはやや大掛かりです。JWTには、ヘッダー・ペイロード・署名という構造や、有効期限(exp)などのクレーム管理が伴うため、単純な共有リンクであれば、まずはランダムトークンかHMAC署名付きURLを検討するのが実務的です。
実際に設計する際は、次の5つを組み合わせて考えます。
このうち①②⑤の「トークンを発行し、DBで期限・状態を管理する」という考え方は、 セッション管理の基本 で扱っているセッションIDの管理方法と同じ発想です。あわせて確認すると理解が深まります。
名称がいろいろ出てきましたが、開発者に伝える際に覚えておくべきものは、次の2つで大体通じます。
「期限付きランダムトークン方式」 または 「署名付きURL(HMAC)」
用途別に整理すると次のようになります。
また、どの方式を使う場合でも、トークンや秘密鍵が漏れた場合に備えて 多要素認証(MFA)とは?導入のポイント のような多層の防御を組み合わせておくと、被害を最小限に抑えられます。
共有リンクやワンタイムURLの安全性は、「ランダムであること」と「書き換えを検知できること」という2つの性質で決まります。
迷ったときは、単純な一時共有にはランダムトークン+有効期限+DB保存、値の改ざんを検知したい場合にはHMAC署名付きURLという基本方針で考えると、実装の判断がぶれにくくなります。