OAUTH
札に棚の番号を書いておくと、その札は誰にでも書ける。
あなたの店には棚がある。そこに「外の店のデータを見にいく鍵」を置いておくと、店が代わりにデータを取ってきてくれる。
鍵を置くには、いったん外の店へ行って「この人にどうぞ」と許してもらう必要がある。行って、許してもらって、帰ってくる。帰ってきたときに どの棚に置くのか を思い出さないといけない。
そのために 引換札 を持って出かける。
むかしの札は、棚の番号がそのまま書いてあるだけだった。しかも読める字で。
札を見れば「7 番の棚」と分かる。帰ってきた店の人は、札の 7 を読んで 7 番の棚に鍵を置く。
札には印もハンコもない。読めるということは、書けるということでもある。
悪い人は、よその人の棚の番号を書いた札を自分で作る。その札を持って外の店へ行き、自分の鍵を許してもらう。
帰ってくると、店の人は札に書かれた番号を読んで、よその人の棚に 悪い人の鍵 を置いてしまう。
棚の持ち主から見ると、自分の棚に知らない鍵が入っている。以後その棚が持ってくるのは 悪い人のデータで、自分のデータはもう出てこない。
札に書くのを、意味のないでたらめな長い番号に変えた。札を見ても棚は分からない。
「この番号は 7 番の棚」という控えは、店の中の帳面だけが持つ。帳面に書き込めるのは、棚の持ち主だと確かめてもらった人だけ。
これで、外から札を作っても帳面に載っていないので、店の人は「知らない番号です」と言って断れる。
札は 1 回きりのはずだった。だから店の人は、帳面を 読んで、それから 消す。
けれど「読む」と「消す」は別々の動作だ。ふたりが まったく同じ瞬間に同じ番号を出すと、消される前に両方が読めてしまう。1 回きりの札が、2 回使えたことになる。
順番を変えた。帳面を読む前に、「この番号は私がもらいます」と 1 回だけ書ける欄に名乗り出る。
この欄は、先に書いた人しか書けない。同時に来ても、書けるのは必ずひとり。書けなかった人はそこで引き返す。
「読んでから消す」ではなく「取ってから読む」。動作の順番を入れ替えただけで、すきまが消える。
ここまでで、他人の棚を狙う攻撃は消えた。けれど向きを逆にした攻撃が残っている。
悪い人は、今度は正直に 自分の棚の札 をもらう。ちゃんと帳面に載った本物の札だ。そしてその札を、よその人に渡して言う。「これを持って、鍵を預けてきて」。
言われた人は、自分の鍵を許してもらって帰ってくる。店の人は札を読み、帳面のとおり 悪い人の棚 にその鍵を置く。
札は本物。帳面も正しい。誰が持ってきたかを、誰も見ていないだけ。
直し方は、映画館のもぎりと同じ。札を 半分にちぎって、片方を出かける人のポケットに入れておく。
帰ってきたら、持ってきた札とポケットの半券が合うかを見る。合わなければ断る。
悪い人から札を渡された人のポケットには、合う半券が入っていない。ちぎってもらった覚えがないからだ。それで止まる。
半券はポケットの中にあり、外から取り出す道はない。同じ店に帰ってきたときだけ、そっと出てくる。
| クロークの引換札 | 認可フローの照合用トークン |
| 札に書かれた棚の番号 | クライアントが組み立てた、署名のない値 |
| でたらめな長い番号 | 暗号論的乱数で作った不透明な値 |
| 店の中の帳面 | サーバ側の一時保存 |
| 1 人しか書けない欄 | アトミックな 1 回きりの確保 |
| ポケットの半券 | ブラウザに置く短命の識別子 |
| 札を渡してこさせる | 逆向きのクロスサイトリクエストフォージェリ |
| 棚に置く鍵 | 外部サービスのアクセストークン |
札から意味を消して控えを店に隠した。取る動作を 1 回にまとめた。残っているのは、出かけた人と帰ってきた人が同じかを誰も見ていないこと。だから札を半分にちぎって、片方を持たせて出す。
3 つの問題は、どれも「札そのもの」の話ではなく 札の周りにある約束の話だった。偽造は札の中身を空にして直り、二重使用は動作の順番で直り、持ち主のすり替えは札の外に置いた半券で直る。