いますぐ使えるOpenID

第5回 Railsで作るOpenID対応アプリケーション実践(後編)

この記事を読むのに必要な時間:およそ 5 分

ホワイトリストを使ってログインを許可するOPを制限する

連載の始めに,OpenIDの特徴は認証サーバが分散していることと言及しました。特にOpenIDはX.509における認証局のような特定の権威を持たない仕組みであるため,誰もがOP(OpenID Provider)になることができます。極端に言えば,OpenIDライブラリに付属するOPのサンプルを起動して,それでログインすることもできるのです。そのため,OPをどうやって信頼するか,というreputation(評価)問題があります。

参考:
OpenID Providerのreputation問題,AOLの方針など - Yet Another Hackadelic

この問題は,RPがOPにどこまでのレベルを期待するかによって,対策が変わってくるように思います。単に,利用者の同一性を確認できればいいのであれば,OPが信頼できるかどうかはあまり重要ではないでしょう。利用者が怪しいOPを使って第三者になりすまされたとしても,それは利用者の責任,ということになります。一方で,botによるユーザ登録を防ぎたいという場合は,それなりに信頼できるOPを選定するか,さもなければRPが自前でbot防止の仕組みを用意することになります。サービスがコミュニティサイトの一面を持つのであれば,後者の場合が多いのではないでしょうか。

OPを識別する方法

今回はOPを制限する一つの方法として,先ほど紹介した参考サイトでも述べられているホワイトリストを用いたログインを実装します。ホワイトリストとは,あらかじめRPが信頼するOPを決めておき,そのOP経由のログインのみを許可する方式です。OpenIDの誰もが自由に使えるIDという理念からは少し外れますが,OPを客観的に審査する方法が無い現状では,ホワイトリストは現実的な解の一つかもしれません。

さて,ホワイトリストを実装するにあたって,ログイン時にOPをどうやって識別すればよいでしょうか。すぐに思いつくのは,利用者のOpenIDアカウント名(User-Claimed Identifier)に含まれているドメイン名で判定することですが,これはあまりいい方法ではありません。なぜなら,OpenIDアカウント名とOPのドメインは必ずしも一致するとは限らないからです。例えば連載の第1回で紹介したyahoo.comの例では,RPへ通知するIDをユーザが選択できました。選択できるURLのドメインは,yahoo.comだけでなくYahoo!が運営する写真共有サイトであるflickr.comも含まれます。つまりOPのドメインがyahoo.comでも,利用者のOpenIDアカウント名のドメインはflickr.comの場合もあるということです。そこでOPの識別にはUser-Claimed Identifierではなく,OP Endpoint URLを用います。OP Endpoint URLは,OpenIDでのログイン時にOPが認証要求を受け付けるURLです。

図6 OP Endpoint URLとUser-Claimed Identifier

図6 OP Endpoint URLとUser-Claimed Identifier

ちなみに,OpenIDアカウント名からOP Endpoint URLを見つける方法については,連載第3回でご紹介したdiscoveryをご覧ください。以下のように,yahoo.comとflickr.comのどちらでログインする場合でも,OP Endpoint URLは以下のように同じURLとなります。

$ ./discover flickr.com
==================================================
Running discovery on flickr.com
 Claimed identifier: http://flickr.com/
 Discovered services:
  1.
     Server URL  : https://open.login.yahooapis.com/openid/op/auth
     Type URIs:
       * http://specs.openid.net/auth/2.0/server
       * http://specs.openid.net/extensions/pape/1.0

$ ./discover yahoo.com
==================================================
Running discovery on yahoo.com
 Claimed identifier: http://www.yahoo.com/
 Discovered services:
  1.
     Server URL  : https://open.login.yahooapis.com/openid/op/auth
     Type URIs:
       * http://specs.openid.net/auth/2.0/server
       * http://specs.openid.net/extensions/pape/1.0

ログイン処理の改造

ホワイトリストを管理するために,TrustedServerというモデルを追加します。TrustedServerにログインを許可するOPのEndpoint URLを登録しておき,ログイン時にホワイトリストに含まれるかどうかをチェックします。Sessionsコントローラのcreateアクションから呼ばれる,open_id_authenticationメソッドを以下のように修正します。先頭の + が追加した行で, - が削除した行です。

@@ -25,9 +25,13 @@
   protected
   def open_id_authentication
     # OpenID でユーザを認証する (begin, completeの両方に対応)
-    authenticate_with_open_id do |result, identity_url|
+    authenticate_with_open_id do |result, identity_url, sreg, open_id_response|
       if result.successful?
-        if @current_user = User.find_by_identity_url(identity_url)
+        server_url = open_id_response.endpoint.server_url
+        if not TrustedServer.find_by_url(server_url)
+          # 信頼していないOPでのログインは拒否する
+          failed_login "Sorry, untrusted OpenID server: #{server_url}"
+        elsif @current_user = User.find_by_identity_url(identity_url)
           # 認証成功でユーザが登録済みの場合はログイン成功 (A)
           successful_login
         else

OpenIDレスポンスのOP Endpoint URLがTrustedServerに含まれていなければ,エラーメッセージを表示してログインを拒否します。

図7 信頼しないOPでログインした時のスクリーンショット

図7 信頼しないOPでログインした時のスクリーンショット

open_id_responseはruby-openidライブラリが提供するオブジェクトで,OpenIDレスポンスのデータを管理します。このオブジェクトのendpoint.server_urlを呼び出すことで,OPのEndpoint URLを取得できます。RailsのOpenID Authenticationプラグインは,標準ではOpenIDレスポンス(open_id_response)をコントローラへと渡さないため,下記のように引数にopen_id_responseを加えています。

@@ -106,13 +106,13 @@
 
       case open_id_response.status
       when OpenID::Consumer::SUCCESS
-        yield Result[:successful], identity_url, OpenID::SReg::Response.from_success_response(open_id_response)
+        yield Result[:successful], identity_url, OpenID::SReg::Response.from_success_response(open_id_response), open_id_response
       when OpenID::Consumer::CANCEL
-        yield Result[:canceled], identity_url, nil
+        yield Result[:canceled], identity_url, nil, open_id_response
       when OpenID::Consumer::FAILURE
-        yield Result[:failed], identity_url, nil
+        yield Result[:failed], identity_url, nil, open_id_response
       when OpenID::Consumer::SETUP_NEEDED
-        yield Result[:setup_needed], open_id_response.setup_url, nil
+        yield Result[:setup_needed], open_id_response.setup_url, nil, open_id_response
       end
     end

なお,実際の運用ではログイン時にリストボックスなどで許可するOpenIDの一覧を選択できるようにしておいた方がよいでしょう。

まとめ

今回はパスワード認証との併用,ホワイトリストによるOPの制限方法をご説明しました。RPによっては,OpenIDを使う場合にいろいろと検討することがあります。次回は,これまでの連載のまとめとしてOpenIDの課題や使いどころについて整理したいと思います。

著者プロフィール

松岡浩平(まつおかこうへい)

NTTコムウェア株式会社にて,オープンソースを活用した認証システムの開発を担当しています。ここ2年は,情報セキュリティ大学院大学に通学しながら,OpenIDを使った認証システムについて研究していました。