さまざまなサービスがOpenID対応を打ち出し、注目度は高まる一方です。しかし、セキュリティの観点ではどのような課題があるのでしょうか。第4回では現時点でのOpenIDセキュリティ対策を詳細に解説します(編集部)
前回はConsumerサイトを実際に作る際のプログラミングに関してお話ししましたが、今回はOpenIDに関するセキュリティについて考えてみます。
今回取り上げるトピックとしては、
などを段階的に説明していきます。IdPの構築方法を知る前にOpenIDプロトコルのセキュリティに関して熟知しておきましょう。
ここまで詳細に解説してきませんでしたがOpenID認証プロトコルのフェイズにおいて、どのようにセキュリティ上の安全性を担保しているかを解説しましょう。
まずはassociateモードを正常に実行するSmartモードの場合です。
ConsumerはユーザーからのClaimed Identifierを受け取ると、associateのキャッシュが存在しない場合は新規にIdPに対してassociateモードのリクエストを行います。第3回で「associateモードは、ConsumerとIdP間で共通鍵を共有するために行う手続きです」と説明しましたが、ここでConsumer—IdP間で共通鍵を共有する手続きを行っています。ここで大事な点は、Consumer—IdP間(さらにUserAgentを介したリダイレクション)でのメッセージのやりとりには必ずHMACを付与して、そのメッセージが改ざんされていない妥当なものであることを確認できるようにしています。
HMACとはkeyed-Hashing for Message Authentication Codeの略で、受信者、送信者ともに同じハッシュ関数と秘密鍵を用いて生成されたコードのことです。HMAC-SHA1ならばハッシュ関数のSHA1を用いたものとなります。
このHMACですが、OpenID Authentication 1.1ではHMAC-SHA1を用いることになっています。さらにいうと、HMACはOpenID認証プロトコルでは2つの使われ方があります。
・checkid_setup/checkid_immediateモードの際に使われるConsumerが生成したIdPへのリダイレクト先URLに付与する署名
ここではConsumerの秘密鍵を用いたHMACが使われる
・id_resモードでIdPから返される認証結果文字列トークンの署名
IdPの秘密鍵を用いたHMAC(Smartモード時)
前者の方は、再びConsumerにリダイレクトされた際に自身の秘密鍵とともに確認すればよいので特に問題はありませんが、後者は認証結果文字列を受け取った際、事前にIdP側の秘密鍵をConsumerが知っていないと確認のしようがありません。もうお分かりかと思いますが、この後者のHMACの照合のために、事前にIdP側の秘密鍵をConsumerに伝える手段としてassociateモードが存在するのです。
このassociateモードで使われている共通鍵の共有手続きはどのようなもので、果たして安全なものなのでしょうか。
associateモードで使われている共通鍵の共有方法はDiffie-Hellman(デフィー・ヘルマン)鍵共有(または鍵交換)プロトコルと呼ばれる方式です。この方式は事前に特別な秘密鍵などを共有することなしに盗聴の可能性のある通信路を使っても、秘密鍵の共有を可能にする暗号プロトコルです。
このプロトコルの要点だけを解説します。
という仕組みです。
いまいちピンとこない方もいるでしょうから、実際にPerlのDiffie-Hellman鍵共有を実装したモジュールを用いて実例を出しましょう。
#!/usr/bin/perl use strict; use warnings; use Crypt::DH;sub default_dh { my $dh = Crypt::DH->new( p => 8995127215884267541995034125870655, g => 2 ); $dh->generate_keys; return $dh; } my $cdh = default_dh(); my $sdh = default_dh(); printf("[consumer] pub: %s, sec: %s\n", $cdh->pub_key, $cdh->priv_key); printf("[server] pub: %s, sec: %s\n", $sdh->pub_key, $sdh->priv_key); printf("shared secret(1): %s\n", $cdh->compute_secret($sdh->pub_key)); printf("shared secret(2): %s\n", $sdh->compute_secret($cdh->pub_key));
実行結果は次のようになります(実行時によって値は異なります)。
$ perl dh.pl [consumer] pub:7802933889436668690536359939538944, sec: 1867083868630338106918631918411838 [server] pub:4449244932917301413656781299646464, sec: 635847400798390060708440216901066 shared secret(1):8768538400044258590443590051168256 shared secret(2):8768538400044258590443590051168256
Consumer、Serverともに異なる秘密鍵、公開鍵ですが、互いに相手の公開鍵を用いて、同じ共通鍵を生成できているのが分かると思います。Diffie-Hellman鍵共有では事前にp値、g値というのを互いに公開していることが前提になります。p値は十分大きい素数で、g値は2以上の自然数です。また生成される公開鍵、秘密鍵は実行ごとにランダムに選ばれます。
この方式では、仮に通信経路で公開鍵が傍受されようともアルゴリズムの性質上、そこから生成される共通鍵を解くのは相当困難という方式なので、まずConsumer—IdP間での通信経路は安全性が確保されているといえます。
さて、HMACやDiffie-Hellman鍵共有によって一見メッセージのやりとり自体の安全性は高いと思いたくなります。しかしちょっと待ってください。実はまだ危険な点があります。
あなたがConsumerだとして、まさにassociateしようとしている相手が本当に正しい相手かどうか確認できているでしょうか。あるいはClaimed Identifierの確認のために実際にユーザーのURLへアクセスしますが、そのホスト名は正確なIPへ正引きできているでしょうか。
そのような危険性に対して神経質なアクセスを行うのがLWPx::ParanoidAgentです。OpenIDの仕様上でも使うことを推奨しています。またホワイトリスト形式やブラックリスト形式にも対応しているので、詳しくはCPANのオンラインドキュメントを見てください。
Copyright © ITmedia, Inc. All Rights Reserved.