本文へ移動

リンクの発行/ Issuing a Link

何を確かめ、何を決め、何を保存し、どう届けるか

共有リンクを 1 本作る操作は、画面の上では「対象を選んで条件を決めて発行を押す」だけです。その裏では、対象が本当に渡してよいものかを検査し、推測できない URL を作り、パスワードを取り出せない形に変換し、案内メールを取りこぼさず配送するための段取りを組んでいます。ここではその中身を順に開きます。

01

発行の流れIssue Flow

上段が業務としての流れ、下段がそれを実現している技術としての処理です。上下は同じ位置で対応しています。発行できるのは、その Box の Editor または Owner だけです。

図 02 · 発行の流れ — 業務と技術の対応
業務の流れ技術の処理STEP 1対象を選ぶファイル 1 件 or フォルダ 1 つSTEP 2条件を決める期限・パスワード・回数・本人確認STEP 3リンクができる推測できない URL が 1 本STEP 4相手に届ける案内メール or 手渡し対象の検証Box に属するか削除されていないか検査済みかアーカイブ配下でないか条件の確定期限の上限を運用値で検査パスワードを scrypt でハッシュ化(処理の外で計算)トークンの生成と保存32 バイトの乱数→ base64url 43 文字share_links へ 1 行追加記録と送信予定audit_logs に発行を記録outbox_events に送信予定を積む下段の 4 つはひとまとまりの処理として実行し、どれか 1 つでも失敗すれば全部なかったことになる。この中で実際のメール送信は行わない — 送信の失敗で発行そのものが巻き戻ってしまうため。

上段と下段は破線で対応しています。下段の右端「送信予定を積む」から先は、この後の「案内メールの配送」で扱います。

02

対象の検証Target Validation

発行の入口で、対象が本当に渡してよいものかを確かめます。ここを通らないものは、そもそもリンクを作れません。

確かめることファイルのときフォルダのとき
Box への所属操作している Box に属していること(別の Box のものは指定できません)
削除されていない論理削除されていないこと
ウイルス検査最新版が検査済みであること配下に検査済みのファイルが1 件以上あること(0 件では発行できません)
アーカイブ配下でない対象自身、または祖先フォルダのいずれかがアーカイブされていないこと

アーカイブは「読み取り専用に凍結した」状態なので、新しく共有リンクを発行することだけを止めます。すでに発行済みのリンクは生き続けますし、無効化はいつでもできます。凍結したものから新たな外部露出が生まれないようにしつつ、既に相手が受け取り待ちのものを勝手に切らない、という切り分けです。

選ぶ画面の側でも止めています: 発行ダイアログの対象ピッカーでは、検査済みでないファイルは選択できない状態で表示し、理由を吹き出しで示します。サーバ側の検証は「画面を経由しない呼び出し」への最後の防壁で、通常の操作でこのエラーには到達しません。
03

設定項目と既定値Settings

設定できるのは 5 項目です。ダイアログは「そのまま発行してよい状態」で開きます。詳細設定は開いた状態で、パスワードは自動生成済み、回数は 3 回、案内メールは送る、が既定です。

項目選べる値既定サーバ側の制約
有効期限24 時間 / 7 日 / 14 日 / 日付を指定(選んだ日の 23:59 まで)7 日上限 14 日。運用中に上限を短くでき、超えるプリセットは選択肢から消えます(押せるのにエラーになる状態を作らない)
パスワード設定する / しない設定する8〜100 文字。既定では開いた時点で 12 文字を自動生成(英大小文字と数字・見分けにくい字は使わない)
ダウンロード回数無制限 / 1 回のみ / 回数を指定(1〜100)3 回ZIP のしきい値を超えるフォルダのときだけ既定が無制限になります(下の注記)
受取人の本人確認する(メールアドレス 1 件)/ しないしない指定したアドレス宛の 6 桁コードで確認します。あとから変更できません
発行時の案内メール送る(宛先 最大 10 件・本文 1000 文字まで)/ 送らない送る宛先を 1 件も入れなければ送信は起きません。本人確認の宛先が送信先に無いときは警告を出します

既定値が「安全側」に寄せてある理由

共有リンクは、設定を甘くするほど楽で、厳しくするほど手間がかかります。空欄から始めると、急いでいる人ほど何も設定せずに発行してしまいます。そこで「推奨の設定で開き、緩めたい人だけが緩める」形にしました。パスワードは自動生成なので、良いパスワードを考える負担もかかりません。

しきい値を超えるフォルダで回数の既定が変わる理由: 合計 200 MB または 10 ファイルを超えるフォルダは ZIP にまとめず、1 件ずつのダウンロードをご案内します。このとき回数は 1 ファイルごとに 1 回消費されます。20 ファイルのフォルダに「3 回」を既定で当てると「3 ファイルしか受け取れないリンク」になってしまうため、この場合だけ既定を無制限にしています。
(画面の画像は掲載していません)
発行ダイアログ。有効期限は「7 日・推奨」が選ばれ、詳細設定は開いた状態、パスワードは自動生成済みで開きます。この下にダウンロード回数(既定 3 回)と案内メール(既定は送る)が続きます。
04

トークンとパスワードの保存Token & Secrets

推測できない URL

URL の末尾に付く文字列(トークン)は、32 バイトの暗号学的乱数を base64url で表した 43 文字です。情報量は 256 bit で、総当たりで当てることは現実的にできません。短縮 URL は提供していません(短くすると当てられる余地が生まれるためです)。

トークンはテーブル全体で重複しない制約を持ちます。無効化したリンクの行も残すため、過去に使ったトークンが再び別のリンクに割り当てられることもありません。

パスワードの保存形

パスワードは scrypt というアルゴリズムでハッシュ化して保存します。scrypt は、計算に時間だけでなくメモリも要求するため、専用ハードウェアで大量に総当たりする攻撃に強い方式です。

設定値意味
計算コスト N163841 回の計算にかかる負荷
ブロックサイズ r8上と合わせて 16 MiB のメモリを要求します
並列度 p1—
ソルト16 バイト行ごとに別の乱数。同じパスワードでも保存値が一致しません
導出する鍵64 バイト—

保存するのは scrypt$N=16384,r=8,p=1$<ソルト>$<ハッシュ> という1 つの文字列です。ソルト用の列を別に持たず、設定値を値そのものに書き込んでいます。この形にしておくと、将来コストを引き上げても古い行はその行に書かれた設定で検証し続けられるため、全件を作り直す移行作業が要りません。

平文は保存も返却もしません。発行者が確認できるのは発行完了画面の一度きりです。また、ハッシュの計算はデータベースの処理の外で行います。CPU を数十ミリ秒使う処理をデータベースの処理の中に入れると、その間ずっと行を押さえたままになるためです。

05

案内メールの配送Invitation Email

案内メールは、発行処理の中で直接送っているわけではありません。発行と同じまとまりの中では「送る予定」を記録するだけにして、実際の送信は別の経路が引き受けます。

図 03 · 処理シーケンス — 発行から案内メールの到達まで
発行者のブラウザmatsuba-api発行PostgreSQLmatsuba-batch払い出しPub/Submatsuba-api取込・送信Resend受取人ここまでがひとまとまり — どれか 1 つでも失敗すれば全部なかったことになる① 発行を実行(対象と条件)② 対象の検証(所属・未削除・検査済み・アーカイブ)③ 32 バイトの乱数からトークンを生成④ share_links へ保存⑤ 監査ログを記録⑥ 案内メールの送信予定を積む⑦ 発行完了(URL とパスワードを表示)⑧ 送信予定を取り出す(1 分ごと)⑨ share_link.created を発行⑩ 配信⑪ 宛先の解決と文面の組み立て⑫ 送信を依頼⑬ 案内メールが届く

⑧ 以降は発行者の操作とは切り離して動きます。仮に配送側で障害が起きても、発行そのものは成立しており、送信予定が残っているので復旧後に配送されます。

この段取りにしている理由

もし発行処理の中で直接メールを送ると、送信に失敗したときに発行そのものを取り消すか、取り消さずに「送ったつもり」で進むかのどちらかを選ばされます。前者は「メール障害でリンクが作れない」、後者は「送ったはずのメールが永久に届かない」になります。

そこで、発行と同じまとまりの中では送信予定を 1 行書くだけにしました。この 1 行はリンクの保存と同時に確定するので、「リンクはあるのに送信予定が無い」も「送信予定はあるのにリンクが無い」も起こりません。あとは払い出し役が予定を拾って送るだけです。

この構成に固有の注意点: 配送経路の途中にある Pub/Sub のトピックが存在しないと、送信予定は数回の再試行のあと配送不能として脇へ退けられます。このとき画面には何のエラーも出ません(発行は成功しているため)。「画面は正常なのにメールだけ出ない」という形になるので、配送経路を新設する順番は「トピックを先に作り、それから送信側を本番へ出す」と決めています。配送不能の滞留は 5 分ごとの監視が拾って管理者に知らせます。
06

共有リンクが送るメールEmails

この仕組みが送るメールは 4 通です。用途によって配送の経路が違います。

メールいつ誰に配送の経路
発行時の案内発行時(任意)指定した宛先(最大 10 件)送信予定 → 払い出し → Pub/Sub → 取込 → 送信
確認コード受取人が本人確認に進んだとき発行時に指定した受取人Resend へ直送(待たせない・6 桁 / 10 分)
総当たりの検知認証の失敗が短時間に集中したとき発行者と管理者送信予定 → 払い出し → Pub/Sub → 取込 → 送信
強制無効化の通知管理者が強制無効化したとき(任意)発行者と Box の Owner管理操作から即時送信(件名・本文を管理者が編集)

確認コードだけ経路が違うのは、受取人が画面の前で待っているからです。ほかの 3 通は数十秒遅れても業務に影響しませんが、確認コードは遅れるとその場で操作が止まります。

案内メールの本文に入るもの・入らないもの

  • 入るもの: 受取 URL / 有効期限 / 残りのダウンロード回数 / 発行者の表示名 / 発行時に入力したメッセージ
  • 入らないもの: パスワード。代わりに「パスワードは別の連絡手段で発行者からお受け取りください」という固定の案内が入ります(パスワードを設定したときだけ)
  • 配信停止のリンクは付きません(社外の受取人はアプリの通知設定を持たないため)

発行時に入力したメッセージは、改行を保ったまま本文に差し込みます。差し込みの際は先にエスケープしてから改行を変換するので、メッセージに HTML を書いても本文には効きません。

(画面の画像は掲載していません)
発行時の案内メール。発行者名・入力したメッセージ・有効期限・ダウンロード可能回数と、受け取りボタン。パスワードは本文に載せず、別の連絡手段で受け取っていただく旨の案内だけが入ります。
(画面の画像は掲載していません)
確認コードのメール。6 桁のコードと 10 分の有効期限、心当たりが無い場合は破棄する旨の案内。
07

発行の完了After Issuing

発行が終わると完了画面が出ます。この画面にはここでしか手に入らないものがあるため、作りにいくつか仕掛けがあります。

要素内容
共有 URLコピーボタン付き。あとから一覧画面でもコピーできます
パスワード設定したときだけ表示。この画面を閉じると二度と表示できません(サーバに平文が無いため)。伏せ字と表示切り替え、コピーボタン付き
案内文のコピー対象名・URL・「いつまで有効か」・パスワードや本人確認がある旨、をまとめた定型文を 1 クリックでコピー。パスワードそのものは含みません
設定内容期限・回数・本人確認の宛先・案内メールの送信件数を確認できます
閉じ方Escape キーや画面外のクリックでは閉じません。「閉じる」を押したときだけ閉じます

案内文のコピーを用意したのは、「URL だけコピーして、期限や注意書きを毎回自分で書き足す」手間をなくすためです。同時に、その定型文にはパスワードを入れないことで、URL とパスワードを同じメールに貼ってしまう事故を仕組みとして防いでいます。

(画面の画像は掲載していません)
発行完了画面。共有 URL、一度だけ表示されるパスワード、案内文のコピー、設定内容の確認。