2013年5月26日日曜日

YubiKey Yubico OTP

今回は Yubico OTP について調べてみようと思います。

Yubico 公式サイトのサポート - ドキュメントページに色々と最新のドキュメントが列挙されています。

Documentation
http://www.yubico.com/support/documentation/

YubiKey Overview から最新の YubiKey Technical Manual をダウンロードしておきましょう。これは、YubiKey の基本仕様などをまとめた文書で、最新の YubiKey などの解説も含んでいるようです。英語が苦手な人も40ページ程なので、一通り目を通しておくと良いかもしれません。




この文書の中では、Yubico OTP についても記述されています。

具体的には以下の項が参考になるでしょう。
2.1.4 The Yubico OTP part
6.1 The Yubico OTP generation algorithm

これを読んで解るのは、OTP部分には、AES-128 暗号と、Modhex と呼ばれる聞きなれないエンコーディングを使っている 128bit(16byte) のデータのようですね。16進数の文字列にした場合、16byte だと32文字になるわけですが、Yubico OTP のデータは例えば以下のように

ccccccccccccjnlfultlibcdielkfdgbhcdefincvrdk

44文字のデータが出力されています。ということで、OTP部分以外が12文字(6byte分)存在していると推測されるようですね。

Modhex については、先の文書に解説があります。

6.2 Modified Hexadecimal (Modhex) encoding

単純に16進数の文字列を変換表で置き換えるだけのシンプルなもののようですね。別段、特殊な演算をするわけではないため、デコードする場合も変換表をそのまま逆に使えば良いことになります。

0123456789abcdef <-> cbdefghijklnrtuv



ちょっと、別な面から調べてみます。YubiKey Personalization Tool で Yubico OTP を Advanced で設定できる内容をみてみると、以下の様な項目が存在しています。


Public Identity (1-16 bytes Modhex)
Public Identity Length (6 bytes is default length as required by Yubico OTP validation server)
Private Identity (6 bytes Hex)
Secret Key (16 bytes Hex)

また、Challenge-Response の Yubico OTP だと Public Identity が設定項目として存在しないことが解ります。




Public Identity は Yubico OTP validation server つまり、YubiCloud などで使う場合に指定するようで、6byte の公開 ID として付けるようですね。Public Identity 付き Yubico OTP を設定した場合、先頭に設定した Public Identity が付与された形で44文字が出力されました。もし、Public Identity なしで設定した場合、ランダムな32文字に変わります。つまり、OTP部分以外の Public Identity が先頭12文字に付与されることがあり、Yubico OTP の実体は 32文字ということが解りました。

Yubico OTP := [Public Identity 12文字(6byte)] + OTP 32文字(16byte)


この OTP部分が AES128 で暗号化されており、復号には秘密鍵(Secret Key)を使用する必要がありそうです。また、複数の YubiKey を判定する場合においては、秘密鍵を特定するために Public Identity や YubiKey のシリアル番号を使って特定する。という仕様を推測します。


あとは、プログラマであれば、具体的なコードを読み進んでいくほうが理解が早そうです。

Low-Level library
http://www.yubico.com/develop/open-source-software/low-level-library/

から Yubico Libraries に含まれる Java Library: “yubico-j” project を見てみましょう。

このプロジェクトに含まれるクラスは少なく非常にシンプルです。大雑把に言うと、チェックサム用の CRC16 や Modhex などの補助関連のクラスと、解釈クラスである Pof、解釈した結果用コンテナである Token、あと、Configuration クラスがありますね。

解釈のインターフェイスは以下の様な感じとなっており、

Token Pof.parse(String block, byte[] key)

OTP 文字列と秘密鍵を渡せば、単純に解釈結果のコンテナが返るようです。システムに対して認証機能を組み込む際には、これらをそのまま使えば良さそうですね。

早速、例に挙げた Yubico OTP を解釈させてみます。Yubico OTP の設定は、先に挙げた設定のように全て 00 で埋めている秘密鍵を使っています。以下の様なコードで試してみました。

String otp = "jnlfultlibcdielkfdgbhcdefincvrdk";
byte[] key = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
              0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Token token = Pof.parse(otp, key);

注意点として、otp のパラメータは Public Identity は含めていない。ということです。組み込む場合、この部分なりで秘密鍵の特定をするわけですし、そもそもに OTP 部分ではないためです。

得られた token から toString() で得た文字列が以下です。(読みやすいように改行を入れました)

[Token
 uid: 0,0,0,0,0,0,
 counter: 1,0,
 timestamp (low): d2,6a,
 timestamp (high): 38,
 session use: 0,
 random: 5b,bd,
 crc: 40,78,
 clean counter: 1,0,
 CAPS pressed: false]

最初に出てくる counter は SessionCounter のようです。clean counter が useCtr に該当するものと考えると、ドキュメントの 6.1 にある定義に沿ったデータが抜き出せているように思われます。

この時点で全て 0 として定義していた Private Identity が uid として確認できていることも解ります。

試しに、otp のパラメータの文字を適当に変えてみたり、入れ替えてみたりすると、以下の例外を吐かれてしまいました。

Exception in thread "main" java.lang.IllegalArgumentException: CRC failure

つまり、Token の生成時点で検証までされている。ということのようですね。解釈の大まかな流れは、OTP の復号化、CRC チェック、要素の分解で、詳細な実装はソースコードにズバリ書いてあるわけですから、開発環境でデバッグ実行したほうが理解が早いでしょう。


Yubico OTP の生成方法については、ドキュメントで説明されている各項目から、概要が把握できるわけですが、YubiKey 自体が持つ内部タイマーによる 3byte のタイムスタンプと、2byte の乱数などが中身を大きく変える部分と言えるでしょう。末尾に CRC16 のチェックサムが付与され、更に AES128 の共通鍵を使って暗号化する流れのようです。



では、視点を変えて、Yubico OTP で運用しているシステムに対して攻撃することを考察し、感覚としてどういった強度があるのか見てみましょう。

まず、ウェブ上で流れていた Yubico OTP らしき文字列を盗聴なりできたと仮定します。何パターンか取得することで、Public Identity は判定できるでしょう。ただし、そこから先は、AES128 の鍵をこじ開けなければ、調べようもありません。面白いことに AES / Rijndeal (ラインダール) 暗号は、適当なキーを使って復号化しても、それが正解であるか判定はできません。つまり、元の内容を知っているか、正しい鍵を持っていない限り、正解にたどり着くのは非常に無茶な内容ということが解るでしょう。

では、攻撃方法を変え、Yubico OTP の内容に沿った適当な内容で OTP を生成して、サーバに投げる事を考えてみましょう。この方法では正しい Private Identity を知り得ないため、その時点でまず破綻しています。更には AES 暗号で鍵をかけなければなりません。しかし、適切な鍵を知らない状態で生成した OTP をサーバに投げた所で全て弾かれてしまうでしょう。

どうでしょうか?何にしても、AES が破られない限り、安心できる方法と思われます。ウェブで使う場合は、SSL を併用すれば途中経路に漏れる可能性は非常に抑えられるでしょうし、他のパスワードのみの状態と比べて非常にセキュリティの強度は固いと想像します。

おそらく、攻撃の糸口となりうるのは、サーバ自体の認証用マスタ情報がセキュリティの不備で漏れたり、内部の管理者による犯行だったりでしょうね。もし、正面からこの認証方法が破られるようなら、AES256 等のより高いbit数の暗号方式に切り替えるか、あとは抜本的な方法として、公開鍵暗号なりを使った認証に切り替えるぐらいでしょうか?ただ、公開鍵を使う場合、電子証明書の有効期間や、ルート証明書、認証局といった環境整備が非常に面倒な上、トータルの運用コストなどを考えるとお手軽感は全く無いでしょう。全てにおいてセキュリティを最優先しない限りオススメしません。



さてさて、本題の Yubico OTP に戻りましょう。ここまで調べがついた訳ですが、ウェブシステムの認証に Yubico OTP を使った、2要素認証を組み込む事を考えると、今までの情報から以下のような情報登録や管理が必要となりそうですね。

個人情報用
  UserID
  Name
  Password

YubicoOTP用
  Public Identity 
  Private Identity
  Secret Key

YubiCloud などの Yubico OTP validation server だと、この情報と、その場で出力した Yubico OTP なども登録するようですね。確かにその場で入力情報の検証を行える方が良いので、OTP も入力させる方が良い流れと言えますね。これで、基本的な2要素認証は可能なのではないでしょうか?


では、YubiKey の盗難などを考慮せず、利便性を優先して1要素認証を前提に Yubico OTP を考えてみます。もし、YubiKey の設定管理などの統制をシステム管理者側で行えるのであれば、Public Identity や Private Identity は重複しない形で好きな様に割り当てできるため、単純に1要素だけで問題なく YubiKey やユーザの特定が可能と言えるでしょう。管理側で統制できない場合は、Public Identity の重複については登録を禁止するなどの制限を行うべきでしょう。Private Identity の重複があることはセキュリティ上、絶対に伝えるべきでは無いと考えます。


最後に、Yubico OTP と、OATH-HOTP を比べてみましょう。Yubico OTP は、HOTP と比べて、カウンタなどを考える必要が無くなるため、持つパラメータが少し多いことを除けば、システム実装は非常に容易と言えるでしょう。特に、トークンとの同期を取らなくて良いのは非常に楽です。YubiKey の故障や紛失へのシステム的補助は両者とも必要だと思いますが、現実的な運用を踏まえたシステム構築を行うのであれば、Yubico OTP の方が有利といえるでしょう。

RFC などの規格に準拠したシステムを使いたい/作りたい要望があれば別かもしれませんが、単純に YubiKey を使ってウェブの認証システムを構築する必要が生じた場合、私なら Yubico OTP を選択するでしょう。



ここ1週間ほど YubiKey に関して、設計、実装、運用などに必要な技術情報を調べてみたわけですが、私自身が必要とする内容は全てメドが付いたため、これで一旦調査を終了したいと思います。実のところは、NFC 対応の YubiKey NEO の事も調べてみたいのですが、今のところ品切れらしく、これについては入手できる機会があれば、といったところですね。

0 件のコメント:

コメントを投稿