リフレッシュトークンの概要とメリット

武井宜行氏:次に、リフレッシュトークンの役割について説明します。リフレッシュトークンはOAuthの中で超重要です。ちなみに、今まですごくいろいろなフローを説明しました。認可コードフロー、Implicitフロー、クライアントクレデンシャルズフロー。でも、基本的にはOAuthは最初に説明した認可コードフローさえしっかり押さえておけば怖くないので、先ほどの資料を読み返す時には、まず認可コードフローをじっくり読んで理解してもらえればと思います。

Implicitフローやその他はおまけみたいなものなので、認可コードフローさえしっかり押さえておけば大丈夫ということを説明しておきます。リフレッシュトークンには有効期限が設定されていて、万が一盗まれても有効期限が過ぎると使えないので、超セキュアです。これはOAuthの超大事なポイントの1つです。

(スライドを指して)リフレッシュトークンの概要は以下のとおりです。これはよくあるリフレッシュトークンの説明ですが、アクセストークンは超短命で、それゆえに漏洩した時のうんぬんとか、すごく長く書いてあるサイトが多いんです。

正直、これを読んでもよくわからないので、リフレッシュトークンがない場合とある場合で、どんな点が違うのかを比較してみます。

ここで思い出してほしいんですが、認可コードフローで説明した、つぶやくところ。これはアクセストークンが取得済みであることを前提とします。

ここから、リフレッシュトークンがない世界では、どんな不便なことが起きるかについて説明します。

まず、リフレッシュトークンがない世界で、Aさんが「白金なう」とつぶやきました。

ここまでは同じです。アクセストークンが渡されて、FacebookとTwitterの両方に投稿が反映されました。

もう一度Twitterでつぶやいてみました。でもアクセストークンには有効期限がある。だいたい1時間くらいが多いですが、有効期限が切れてエラーになってしまいました。

しょうがないので、もう一度Facebook(認可サーバー)での認証からやり直して、アクセストークンを取得するなんてことになると、超面倒くさいですよね。例えばアクセストークンの有効期限が1時間だったら、1時間ごとに認証しなきゃならないって、すごく不便だと思います。ここで、リフレッシュトークンの出番です。

例えばアクセストークンを取得する時、認可コードを渡してアクセストークンが返ってくる時に、アクセストークンとリフレッシュトークンが両方返ってきます。

(スライドを指して)実はこんなかたちのJSONが返ってくるんですが、この時access_tokenのフィールドにアクセストークンが返ってくると同時に、このrefresh_tokenのフィールドにも実はリフレッシュトークンが一緒に返ってくるんです。

このリフレッシュトークンも、一緒にクライアントであるTwitterの中に保存します。アクセストークンとリフレッシュトークンを取得した状態で、Twitterで「白金なう」とつぶやいてみると、同じように反映されました。

有効期限が切れてリクエストがエラーになってしまうと、TwitterはリフレッシュトークンをFacebookのほうに送ります。

リフレッシュトークンを送ると、アクセストークンとリフレッシュトークンがまた再作成されて返ってきます。新しいアクセストークンと、新しいリフレッシュトークンです。そして、もう1回新しく発行されたアクセストークンで発言すると(Facebookに接続すると)めでたく「白金なう」と反映されました。

このように、リフレッシュトークンで随時アクセストークンの有効期限が切れた場合、どんどんリフレッシュしておけば、わざわざアクセストークンの有効期限が切れるたびにFacebookの認証画面で認証しなくても大丈夫というのが、リフレッシュトークンすごくいいところです。

アクセストークンの有効期限を長くするのはダメなのか?

ここで僕は疑問が湧きました。こんな面倒くさい仕組みを使わなくても、アクセストークンの有効期限を6ヶ月とか1年とか、長くしちゃえばいいじゃないかと思ったんです。でも実はこれ、NGなんです。アクセストークンの有効期限を長くするのは、けっこう危険です。なぜ危険かというと、アクセストークンはよくネットワーク上を流れるからです。

AさんがTwitterでつぶやくたびに、Facebookにアクセストークンが流れます。有効期限が6ヶ月のアクセストークンが何回も流れます。

もしハッカーがいて、それを盗聴したとしたら、すごく危険です。アクセストークンの有効期限が長いと、長い間、Facebookにいたずらできてしまうわけです。

でもアクセストークンの有効期限をすごく短くしておけば、アクセストークンはネットワーク上に頻繁に流れるものの、ハッカーに盗聴されて奪われたとしても、1時間後には期限切れになるので、いたずらされるリスクが減るというわけです。

実はリフレッシュトークンも、アクセストークンと同じくネットワーク上を流れますが、アクセストークンに比べて、ネットワーク上を流れる頻度がすごく少ないんです。そのため、盗聴されるリスクもアクセストークンに比べて少ないんです。

有効期限が少しくらい長くても大丈夫だろうという考え方に基づいて、このようなOAuthのシーケンスになっています。

(スライドを指して)今回は以下のポイントについて説明しました。トークンには有効期限が設定されていて、万が一トークンが盗まれても有効期限が過ぎると使えないので、すごくセキュアです、ということでした。

OAuthで使われる2種類のアクセストークン

次に、アクセストークンについて説明します。さっきからよく「アクセストークン」と言っていますが、ここからはけっこう込み入った話なので、資料だけで説明するのはなかなか難しいと思います。資料はあとでじっくり見てもらえればと思うので、今回はざっくりと説明します。

アクセストークンは超重要ですが、OAuthの仕様では基本的にはアクセストークンを定めていません。何でもいいんです。その形式は定めていませんが、主に2つの形式を取ります。

やり取りされる情報に、アクセストークンの中にアクセストークンに関する情報を内包せず、アクセストークンを一意的に識別する識別子のみを入れる形式(タイプA)と、アクセストークンに関する情報をアクセストークンの中に内包する形式(タイプB)です。これだけ読んでもいまいちわからないと思うので、ていねいに説明します。

アクセストークンを一意的に識別する識別子のみを入れる形式

タイプAのパターンです。実はタイプAのパターンは今まで説明していたものなんですが、認可コードフローでおさらいをしたいと思います。

認可コードフローは、まず認可コードを要求して、認可コードが一致していたらアクセストークンを返します。

そこで、AさんがTwitterでつぶやきます。それによってアクセストークンを要求します。そうすると、つぶやきが両方とも反映されます。こんな流れで進んだと思います。

認可サーバーとリソースサーバーが同じならいいですが、別々だったらどうか。別々の場合でもう1回、今のシーケンスを説明します。

認可サーバーが、Azure Active Directoryの中だとします。そしてリソースサーバー、Facebookが分かれてしまいました。今までFacebookで認証していましたが、Azure Active Directoryで認証するようなシーケンスに変わったとします。

そうすると、認可コードを渡して認可コードが一致しているかどうかを確認して、一致していたらアクセストークンを返します。

アクセストークンはAzure Active Directoryのデータベースの中に保存されています。

次に「白金なう」とつぶやきます。

アクセストークンをFacebookにもTwitterの投稿内容を反映させたいので、アクセストークンをFacebookにも送りましょう。

しかし、これはできないんです。Facebookは、アクセストークンを受け取ったとしても、このアクセストークンが正しいかどうかを検証できません。 なぜかというと、アクセストークンはAzure Active Directoryのデータベースの中に保存されているからです。Azure Active DirectoryとFacebookはそもそも別のサービスで、ネットワークも別々、もちろんデータセンターも別々なので、FacebookからAzure Active Directoryの中のデータベースにアクセスできるわけがないんです。

JSON Web Tokenの特徴

そういう場合は、こういう方式だとうまくいきません。そこでJSON Web Tokenというものが登場します。ここでJSON Web Tokenを説明するために秘密鍵暗号化方式、公開鍵暗号化方式、改ざん防止、身分証明を説明しようとしましたが、時間の都合上、できないので、端折らせてください。

JSON Web Tokenの説明をします。

(スライドを指して)本当にざっくりした説明で申し訳ないですが、JSON Web Tokenの特徴は以下です。JSON Web Tokenは、アクセストークンが正しいかどうかをチェックする仕組みです。もっと専門的な用語を使うと、JSONに電子署名を行って、必要に応じてそのJSONの検証を行って、認証可否を決定する仕様です。

OpenID Connectではよく用いられるし、OAuthでも、先ほど言ったAzure Active Directoryなどを使って、認可サーバーとリソースサーバーが別々になっている場合は、JSON Web Tokenがよく使われます。Azure Active Directoryも、JSON Web Tokenが発行されます。

JSON Web Tokenがどんなものかというと、中身はヘッダーとペイロード、ほかにヘッダーとペイロードをピリオドでつないで電子署名をしたものです。

ヘッダーとは何かというと、JSON Web Tokenの署名アルゴリズムやJSON Web Tokenのタイプといった、いわゆるメタ情報みたいなことが書いてあります。

JSON Web Tokenのペイロードとは、ユーザー名やユーザーのメールアドレスなど、本当の本当のユーザー情報が書いてあるところです。

(スライドを指して)実際にJSON Web Tokenは、認可サーバー側ではこのように作られます。まず、ヘッダーとペイロードを作って、これをガッチャンコ、ピリオドでつなぎます。ヘッダーとペイロードをピリオドでつなげたものを、SHA256でハッシュ化します。さらにハッシュ化したものを、認可サーバーに登録した共有鍵で暗号化します。暗号化したものが署名になるので、(スライドを指して)こんなかたちになります。

これを受け取ったリソースサーバーではどうやって検証するかというと、ヘッダーとペイロードをピリオドで結合したIDトークンを、SHA256でハッシュ化します。この署名についているものを、アプリケーションに登録した共通鍵で復号化します。(スライドの②を指して)これはアプリケーションではなくリソースサーバーです。

リソースサーバーと認可サーバーでは、事前に秘密鍵を共有しておくことが大前提です。認可サーバーで秘密鍵で暗号化したものを、同じリソースサーバーに登録されている秘密鍵で復号化して、両方が一致していたら認証OKとする。両方が一致していたら確実に認可サーバーで発行されたもの、つまりこの人を信頼していいということです。

アクセストークンに関する情報をアクセストークンの中に内包する形式

横道に逸れましたが、タイプBのアクセストークンの中に情報を含む場合の説明をしたいと思います。JSON Web Tokenを使った場合に、さっきはうまくいかなかったのに、どうしてうまくいったのか。

認可コードフローをAzure Active Directory、認可サーバーに渡すのは同じです。先ほど説明したように、Azure Active Directoryの認可サーバーとリソースサーバーのFacebookの間に、秘密鍵を事前に共有しておくことが大前提です。

ただ実際は、このあたりに公開鍵暗号化方式というものが使われていて、本当は秘密鍵を事前に共有する必要はありませんが、わかりやすくするために、秘密鍵を事前に共有するという方式で説明します。実際には、Azure Active Directoryはそういう方式を使わないということを理解してもらえればと思います。

そして、認可コードを要求します。

認可コードを要求されたAzure Active Directoryは、自身が発行してデータベースに登録したその認可コードと、その有効期限に問題がなければ、先ほどのロジックに従ってJSON Web Tokenを発行して返します。ここで大きなポイントなのが、JSON Web Tokenの場合はAzure Active Directoryの中のデータベースに保存していないことです。これは大きなポイントです。

さっきと同じように、認可サーバーの中ではこんなロジックでアクセストークンが作られました。

アクセストークンが無事に作成されたので、Twitterで「白金なう」とつぶやきました。

そうすると、JSON Web Tokenで作られたアクセストークンをFacebook側に送ります。

Facebookの中では、先ほどJSON Web Tokenを検証したのと同じように認可サーバーの秘密鍵で署名を復号化して、ヘッダーとペイロードをピリオドでつないだものをSHA256でハッシュ化して、両方が一致していたらOKとします。これで認証がOKになりました。

このシーケンスの中では、お互い共有された秘密鍵によってJSON Web Tokenの検証しか行われていません。うまく言えませんが、要はAzure Active Directoryの中でアクセストークンを保存する必要がない。この秘密鍵さえ共有していれば、JSON Web Tokenを検証することで、認可サーバーで認証したユーザーであることをFacebook側が理解できるんです。

これによって、アクセストークンをAzure Active Directoryの中に保存することなく、Facebookが認可サーバーで認証されたユーザーの正当性を理解できるという、なかなか込み入った仕組みです。うまく説明できたという自信はないんですが、あしからず。

このあとはOAuthを認証に使うことの危険性についてと、stateパラメータによるCSRF対策をしましょうという説明をしようと思ったのですが、両方の説明をするにはさすがに時間が少なそうなので、OAuthを認証に使うことの危険性だけにしたいと思います。

(次回に続く)