Flickr API の認証
tDiary の Flickr プラグインを改造して、Google Mapsの位置情報を簡単にタグに埋め込める (geotagged) ようにしたいと思っている。 そのためには、プラグインから Flickr 上の写真にタグを埋め込まないといけない。 タグを埋め込むには認証が必要…ということで、 Flickr API の認証の仕組みを調べてみた。
情報源は Flickr の公式ドキュメントから。
事前準備 (Flickr API Key)
Flickr の API を使うには、 API Key が必要。 API Key は、サービス (Flickr API を使うアプリケーション) を一意に識別するために使われるみたい。 この API Key は Flickr Services で取得できる。
いちばん簡単な方法
Flickr API では、ユーザの ID とパスワードを使って Flickr API にアクセスすることができる。 たとえば、ユーザのお気に入り (Favorites) 写真の一覧を得る flickr.favorites.getList を使うには、以下の URL にアクセスすればいい。 ID (email) とパスワードは URL の一部として埋め込む。
http://flickr.com/services/rest/?method=flickr.favorites.getList&api_key=API_KEY&email=user@example.com&password=PASSWORD
「API_KEY」には上記で取得した API Key が、「PASSWORD」にはそのユーザのパスワードが入る。 他の API も同じような方法で使うことができる。
この方法はシンプルで分かりやすいけど、実はあまり良い方法じゃない。 API を使うサービスが、ユーザの ID とパスワードを知ることになるから、サービス側が悪意を持てばアカウントを乗っ取ったり、ユーザの写真を全部消したりできちゃう。 それに、ID とパスワードが URL に 入っているから、 Web サーバのアクセスログにも残っちゃう。
そこで、 Flickr には認証のための仕組みが用意されている。
トークンを使った認証
まずはおさらい。 Flickr の認証を考える時の登場人物は3人いる。
- 利用者 (ユーザ)
- Flickr
- Flickr API を使ったサービス (以下サービス)
ここで、「ユーザ」が「サービス」に ID とパスワードを教えちゃうのが問題なんだった。 そこで Flickr API では、代わりに「トークン」を使った認証ができるようになっている。 「トークン」は一種の委任状のようなもので、ユーザ本人に代わって Flickr 上のデータへのアクセスを許可していると思えばいい。
「ユーザ」は「サービス」にトークン (委任状) を発行し、そのトークンを使って「サービス」が Flickr にアクセスするというモデルになる。 こうすれば、「サービス」にパスワードを教えなくてすむ。
ちなみに、トークンは「182657-31ba003033cd22f9」のようなただの文字列。 そのトークンがどういう意味を持つかは、Flickr側で管理しているみたい。
トークン発行の流れ
もう少し詳しく、トークン発行の流れを見てみる。 詳細は、前述の Flickr Authentication API Web Applications How-To が参考になる。
(1) 「サービス」が Flickr の認証画面へのリンクを生成する。 このリンクには、「サービス」の api_key と、必要となるアクセス権限 perm (read, write, delete, none のいずれか) が含まれている。
http://flickr.com/services/auth/?api_key=API_KEY&perm=write&api_sig=SIGNATURE
この例では、 API_KEY というサービスが書き込み権限 (write) を要求している。 api_sig は偽造防止のための文字列。詳しくは後述。
(2) ユーザがこのリンクをクリックすると、 Flickr のログイン画面が表示される。 すでにユーザが Flickr にログイン済みの場合はログイン画面ではなく、このサービスにトークンを発行してもいいかどうかの確認画面が表示される。
(3) ユーザがログインすると、「サービス」の URL にリダイレクトされる。 このとき、 URL の一部に frob という文字列が含まれている。 frob はトークンの引換券みたいなものらしい。
実は、 URL での戻り先は、あらかじめ固定的に決めておく必要がある。 Flickr Services のページで、Authentication の「Edit configuration」のリンクを開き、Authentication Type: を Web Application に、 を戻り先の URL にしておく。 つまり、1つの API Key に対して戻り先は1つだけになる。
もし、 Callback URL が http://exapmle.com/ だとすると戻り先の URL は
http://exapmle.com/?frob=353171-81f28a87419e50e9
などになる。
(4)(5) 取得した frob をキーにして、トークンを取得する。 frob が先ほどの例だとすると、サービスが直接
http://flickr.com/services/rest/?method=flickr.auth.getToken&frob=353171-81f28a87419e50e9&api_sig=SIGNATURE
にアクセスする。 ここでも、 api_sig は偽造防止のための文字列。 この結果は、XML形式でトークンが返ってくる。 これには、トークンの ID, 権限 (perm), ユーザIDが含まれている。
なんで、(3) で直接トークンを渡さないのかはよく分かっていない。 ユーザにトークンを見せないようにするため、かな? ドキュメントのどこかに書いてあるのかも。
ここまで見てきたように、ユーザはサービスごとにトークンを発行することになる (あるトークンを別のサイトにコピーしても使えないようになっている)。 自分がどこにトークンを発行したかは、アカウントページの Authentication listで確認できる。 発行したトークンの取り消しも可能。
トークンの偽造防止 (api_sig)
先ほどの例では、メソッドの呼び出し時に api_sig というパラメータを渡している。 これは、偽のサービスが勝手に Flickr の API を呼び出せないようにするために使われる。
API 呼び出し時には、サービスを識別する「api_key」と、ユーザを識別する「トークン」が Flickr に送られる。 このうち api_key は第三者が簡単に取得できる。 もしユーザのトークンが盗まれると、他の偽サービスがそのトークンに対応するユーザのデータを好き勝手に操作できるようになってしまう。 ユーザのトークンはサービスから Flickr に送られるデータ (HTTP の QUERY_STRING) に含まれるので、盗まれる危険性はそれなりにあると考えた方がよさそう。
第三者にトークンが盗まれても被害を少なくするための仕組みが、「api_sig」という署名になる。
署名の仕組みは比較的シンプル。
まず、サービスと Flickr の間で共通の文字列 (SECRET) を保持しておく *1 。
この SECRET とリクエストデータを連結した文字列をハッシュ化 (MD5) したものが、署名データになる。
第三者がトークンを盗んだとしても、 SECRET が分からないと署名は作れないという仕組み。
最初の取得時を除くと、 SECRET が Flickr とサービスの間でやり取りされることはないため、SECRET 自体が盗まれる危険性はトークンよりも低い。
ただし api_sig とトークンさえ分かれば、 SECRET が分からなかったとしても偽サービスが同じリクエストを送ることができる(リプレイアタック)。 SECRET があるからといって、トークンの管理はしっかりとしなくちゃいけないだろうね。
追記 (2006/5/7)
現状の方式では、ある条件を満たせば SECRET を知らなくても署名値を推測することができるそうです。 独自の方式ではなく、 HMAC を使うべきでしょう。