2要素認証のTOTPとHOTP、どちらがより安全か?

(Last Updated On: 2018年8月18日)

今すぐできる、Webサイトへの2要素認証導入では簡単に2要素認証が導入できること、2要素認証には

  • 時間ベースのOTP(TOTP:Time-based One Time Password RFC 6238)
  • カウンターベースのOTP(HOTP:HMAC-based One-Time Password RFC 4226

があることを紹介しました。Google認証は両方をサポートしています。※

※ 今すぐできる、Webサイトへの2要素認証導入で紹介したライブラリはTOTPのみサポートしています。

TOTPとHOTP、どちらがより安全か?

 

TOTP、HOTPのどちらを導入してもユーザー名とパスワードのみによる認証に比べ、より高いアカウント(認証)の安全性を維持することができます。しかし、TOTPはHOTPに比べ以下の理由で脆弱です。

  • TOTPは特定の時間内なら何度でも利用できる(再生攻撃対策がない場合)

つまり、攻撃者が盗聴を行っている場合、TOTPを使っていても簡単にログインできてしまうことを意味します。HTTPSを使っていても安心はできません。HTTPSを使っていてもMITM攻撃で盗聴が可能です。最近でも不正なアプリ(SuperFishなど)でのHTTPS盗聴が問題になったことは記憶に新しいと思います。MITM以外にもキーロガーにも脆弱です。

ネットワークの盗聴 1はどうしようもないとしても、HOTPの場合はキーロガーが使われていても同じOTPは使えません。このため、TOTPに比べHOTPはより安全です。

今すぐできる、Webサイトへの2要素認証導入で紹介したライブラリはTOTPのみサポートしています。より高度なセキュリティが必要な場合、HOTPを利用した方が良いことは覚えてくとよいでしょう。HOTPを導入する場合、同期がずれた場合に再同期が必要になります。かならず再同期するインターフェースを用意しましょう。再同期はOTPデバイスで2つ以上の連続するOTPを生成させ、生成したOTPを送信してもらい再同期します。

最近は一度しか使えないようにしている実装が多くなってきていると思います。しかし、使用済みのTOTPの保存にはセッション変数は使えません。ユーザーID毎に使用済みTOTPを保存する必要があります。このため、実装していない場合も少くないでしょう。CMSなどのTOTPプラグインなどを使う場合、仕様に注意が必要です。

キーロガーの脅威は以前より高くなっています。例えば、USBとWiFiを使い無線でキー入力を送信可能なデバイスが50ドル程度で米国のAmazonから入手可能です。不十分なTOTP実装の場合、同僚のPCや共有PCへのログイン中に使ったTOTP(とパスワード)を使ってログインできてしまいます。

上の画像が実物のWiFi対応キーロガーです。キーボードのUSBの先に刺さっていても、普通は気付かないでしょう。日本のAmazonでもこの種のキーロガーが購入できます。

HOTPにも問題がある

HOTPの方が安全性は高い、と言っても良いと思いますがHOTPにも問題があります。

  • ユーザーがOTPを利用しない限り、OTPが更新されない

つまり、攻撃者はアクティブでないユーザーのパスワードを知っていれば、OTPをブルートフォース攻撃できることを意味します。6桁の数字なので完全に総当たりをして100万リクエストで確実にアカウントを盗めます。

TOTPの場合、総当たり攻撃をしようとしても、攻撃中にOTPが変わってしまうためアクティブでないユーザーであってもどのくらいのリクエストで攻撃に成功するかは「運」によります。

TOTP/HOTP、どちらにしてもアプリケーションやファイアーウォールなどで異常なアクセスがある場合にネットワーク的に遮断したり、アカウントをロックする(DDoS型でブルートフォースされた場合、特定IPを遮断しても意味がない)など対策が必要です。

OTPの同期がズレてしまうことも問題です。同期がズレることを前提にプログラムを作れば良い、と考えるかもしれません。例えば、サーバー側が保存しているシークエンス+10まで一致していないか?をチェックすれば多少同期がズレても認証できます。しかし、この様な実装にするとブルートフォース攻撃の有効性を高める事になります。(+1なら2倍、+10なら11倍、9万リクエスト程で攻撃に成功する可能性があります)

参考:OTP(ワンタイムパスワード、2要素認証)とタイミング攻撃 HOTPがタイミング攻撃に脆弱だと割と簡単に攻撃できてしまいます。HOTPの第二パスワードを検証する場合、比較時間が同じになるhash_equals()を利用するか、intにキャストしてから比較しなければなりません。

本来ならこういったブルートフォース攻撃は検知して防止できるようにしておくべきます。2017年度版OWASP TOPからA10「不十分なログとモニタリング」脆弱性として登録されています。

HOPT実装で絶対にしてはならないのは、古いOTPの有効化です。同期がズレることがあるので古いOTPも幾つか有効にしてしまおう、としてしまうと再生攻撃に脆弱になります!同期がズレる原因は不安定なネットワーク等で、HOTP生成デバイスのOTPを使ったのにサーバー側に届いていない、といった状態になる時です。古いOTPまで有効化するのは絶対に禁止です。

OTPを入力するフォーム/チェック順序の問題

TOTP/HOTPが利用できるシステムやプラグインが増えてきました。中には実装に問題がある物があります。

  • ユーザーID/パスワード/OTPは同時に入力させ、同時に正当性をチェックしないと意味がない

OTPを入力させるページが正しいユーザーIDとパスワードを入力後に表示させる方式があります。これだと、パスワードの総当たり攻撃を許してしまいます。OTPとパスワードは同時にチェックしなければなりません。2 3

OTPとパスワードのチェック順序にも注意が必要です。

  • パスワードとOTPのチェックは、OTPを先にチェックしないとパスワードのブルートフォース解析を許してしまう

同じフォームでOTPとパスワードを入力しても、パスワードが正しいか先にチェックしてしまうと”タイミング攻撃”4により、パスワード総当たり攻撃が可能になります。OTPが正しいか?既に使われていないか?必ず先にチェックしなければなりません

Google認証でHOTPを利用する

Google認証はTOTPのみでなく、HOTPもサポートしています。以下のQRコードは今すぐできる、Webサイトへの2要素認証導入で紹介したGoogle Chartを使ったTOTPのQRコード生成URLの”totp”を”hotp”に変えただけのQRコードです。

https://chart.googleapis.com/chart?chs=200×200&chld=M|0&cht=qr&chl=otpauth%3A%2F%2Fhotp%2FBlog%3Fsecret%3D2ZDZXLT7CJ3O5M3L

このQRコードをGoogle認証で読み込むと、見慣れないリロードボタン付きのOTPが追加されます。追加したOTPは削除できます。間違って必要なOTPを削除しないよう注意してください。

 

まとめ

TOTPは盗聴(キーロガー)と再生攻撃に脆弱です。しかしHOTPは、簡単に同期がずれる5ので使い勝手が今一つです。ただでさえ2要素認証の利用をユーザーに要求するのはハードルが高いので、TOTPでよしとするのは仕方ない選択と言えるでしょう。

両方とも問題があるなら両方の欠点を無くしたOTP(TOTPとHOTPを組み合わせたOTP)を作れば良いのでは?と考える方も居ると思います。Google認証を使わずに、OTPシステムを自前で構築しても構いません。この場合、RFC 2289が参考になります。少し古いのでハッシュ関数としてMD5やSHA1に言及していますが、いま作るならSHA2/3以上のハッシュ関数を使うべきです。

TOTPでもサーバー側で一度使ったトークンを検出すれば、盗聴による再生攻撃はできなくなります。一手間増えますが、TOTPを使う場合は使用済みトークンは再利用できないようにすると良いです。その際にはセッション変数($_SESSION)6は使えず、ユーザー毎に用意した使用済みトークンを保存するデータベースが必要になることを覚えておいてください。


  1. ネットワークレベルの盗聴できることが前提の場合、再生攻撃でなくても単純にセッションキーを盗むこともできます。盗聴できないようしていなければ安全性は保証できません。TOTPをより安全に利用するには、一度使った有効なOTPは2回以上使わせないようする対策が必要です。キーロガーでキー入力だけを盗んでいる場合には特に有効です。有効なTOTPを一度しか使えないようにした場合、HOTPより安全性が高いです。 
  2. ユーザー体験を優先して「正しいパスワードを入力後に、OTPを入力させる方式」が間違っている訳ではありません。脆弱な設計ですが、この程度のリスクは許容するという選択も可能です。オススメは”ユーザー名”、”パスワード”、”OTP”の入力を表示し、”OTP”を利用していないユーザーの場合は空にするように指示する方式です。タイミング攻撃でOTPが空なのかどうかを判別することも可能ですが、単純な総当たり攻撃の意欲を削ぐには十分だと思います。OTPを利用しないユーザーにも表示することが躊躇われる場合、ユーザー名を入力した時点でユーザー情報を検索し、OTPが有効になっている場合は動的にOTP用の入力を表示するようにすると良いです。 
  3. 2要素認証だからといって、わざわざパスワードの解析が可能な実装にする必要はありません。リテラシの低いユーザーは同じパスワードを使い回しています。ユーザーのことを考えてもパスワードが解析可能な実装にすべきではないでしょう。 
  4. パスワードが一致した場合、OTPのチェックが行われる。パスワードが一致しない場合、OTPのチェックが行われない。このOTPチェックの有無の違いが実行時間の違いとなり、パスワードが一致したのか、しなかったのか?タイミング攻撃で解析可能になります。 
  5. ワイヤレスネットワークなど不安定な通信回線の場合、HOTPを送信した直後に通信が切れるとサーバー側では送信されたHOTPは使用済みとせざるを得ません。この状況に自動的に対処する為には古いHOTPも有効として処理しなければなりませんが、これはHOTPの強さを著しく低下させます。 
  6. セッション変数は”そのセッションでのみ有効”です。セッション変数に使用済みOTPを記録してチェックしてみ意味がありません。共有するmemcachedやDBMSなどを使い、ユーザーIDをキーとし、使用済みOTPを保存しチェックしなければなりません。 

投稿者: yohgaki