ワンタイムパスワード


常に異なる通信内容で、パスワードを送信する

Webアプリケーションを開発するさいに、常に意識しておくことの一つに、通信は盗聴される可能性があるということです。仮に通信を暗号化していても、このことは変わりません。
さらに通信の内容がパスワードといった、第三者に知られては困るものの場合は、特に注意が必要です。
そこでワンタイムパスワードの概念を導入して、同じ内容を送信する場合でも、常に通信内容が異なる方法を考えようと思います。

概要

平文のまま送信しない

まず初めに考慮しなければいけないことは、平文のまま通信しないことです。
平文のまま送信 図のように平文のまま通信してしまうと、通信を盗聴された時にパスワードがまる見えになってしまいます。そのあとで、どのように工夫しても意味がありません。
なので、必ず符号化します。
暗号化して送信 図のように入力されたパスワードを符号化することで、パスワードを推測しにくくします。

毎回同じ値を送信しない

次に考慮しなければいけないことは、毎回同じ内容を送信しないことです。
毎回同じ内容を送信 毎回同じ内容で送信されることがクラッカーに知られると、パスワードを知らなくても送信内容そのものを偽装することで、パスワードを取得したことと同様のことが起こってしまいます。
そこで、毎回異なる値(トークン)を使ってパスワードを符号化します。
トークンを使って符号化して送信 時刻を基準にした値など、送信するたびに異なるトークンを使うことで、パスワードにトークンを結合した符号化前のフレーズは、必ず毎回異なるものになります。
そのため、符号化された値も毎回異なることが期待できます。

トークンの同期方法

では、どのようにトークンを作ればよいのでしょうか。
トークンは、クライアントとサーバで同じ値を使わなければいけません。異なっていた場合は、不正アクセスの可能性がありますので、処理を続けてはいけません。
銀行などのセキュリティーに神経をとがらせているところでは、トークンデバイスを配布して、デバイスの提示する値を入力させることがあります。
費用のことを考えなければ非常に強力な手法と思います。しかし、費用にシビアな場合には利用できません。そこで、ソフトウェアで対処する方法を検討することにします。

サーバからもらう

サーバとクライアントでトークンの同期をとる際の仕様を非常に単純にできます。
一方、トークンをサーバから受け取るので、受け取ったトークンを無加工で利用してはいけません。あらかじめ暗号化しておくとか、受け取ったトークンを再度符号化するなど、何らかの加工をする必要があります。

時刻をもとに計算する

トークンはクライアントでも作成しますので、トークンを盗聴することが出来ません。そのため強度が少し上がります。とは言っても、トークンの計算式を解析される可能性は残っています。
さらに、サーバとクライアントでトークンの同期をとる方法は、非常に複雑になります。
例えば、サーバとクライアントの時計がずれていたらどうなるでしょうか?
数秒ならともかくとして、1時間もずれていたら決して認証できません。いや、できてはいけません。
(Windowsの時計が、1時間程度ずれることはよく経験します。)

他にもトークンを同期する方法はあると思います。しかし今回は、比較的簡単に実装できる、サーバでトークンを作成してクライアントに送信する方法で検討を進めたいと思います。

仕様

要件

Webサービスのサインインなどで送信するパスワードを毎回異なる値になるようにする。
クライアントでは、Javascriptなどのプログラムが実行できる。

基本設計

パスワード認証時

パスワード認証のフロー
図のように、認証リクエストの直前にサーバからトークンを取得しています。
このトークンの有効期限は、1分としておきたいと思います。
トークンを取得してから認証のリクエストを送信するまでの時間を最短にすることで、トークンが古くなってしまうことに起因するエラーを回避するとともに、トークンの有効期間も短くすることに貢献しています。
しかしトークン作成時と認証時で時間差があるため、運悪くトークンが古くなってしまい、エラーとなることがあります。その際のエラーを回避する目的で、1回のリトライを行っています。

パスワード登録時

パスワード登録のフロー 図のように、登録リクエストの直前にサーバからトークンを取得しています。
今回は、取得したトークンを暗号化のキーに使います。
ここで注意したいのは、サーバがリクエストを受け取った時点で、トークンが古くなっている可能性があることです。
トークンが古くなってしまっていた場合の判定のために、クライアントではパスワードに現在時刻を結合する処理を行っています。
もし、受け取った暗号文を復号して現在時刻が正しく読み出せなければ、トークンが古くなってしまったものと判断できます。
ところで、復号の成否を判定するキーワードに時刻ではなく固定の文字列を使うとどうでしょうか?
成否の判定は簡単になりますが、通信を盗聴された場合に解読されやすくなります。そのため、固定の文字列を成否判定のキーワードにすることは止めたほうがよいでしょう。固定でなければよいことですので、現在時刻より優れたキーワードを探してみてください。