ワンタイムパスワードの仕組み

エントリー



実践が示すように、ワンタイムパスワードの操作の原則についての理解がある程度欠如しています(これらは、GMail、支払いシステムの特別なトークンなどで使用されるものです)。



この短い記事を読んだ後、ハッシュに基づくワンタイムパスワードの仕組みを理解し、同時にPythonでGoogleの2段階認証のパスワードを計算できる小さなプログラムを作成します。


ハッシュ関数



ハッシュ関数を使用すると、任意の長さのデータを取得し、それらに短い「デジタル指紋」を構築できます。 ハッシュ関数値の長さは、ソーステキストの長さに依存しません。 たとえば、一般的なSHA-1アルゴリズムの場合、このフィンガープリントの長さは160ビットです。



値が常に同じ長さで、ソーステキストに依存しない理由を理解するために、ホイール付きコードロックの形式でハッシュ関数を単純化できます。 最初にすべてのホイールを「ゼロ」に設定してから、テキストを調べて、いくつかのルールに従って各文字のホイールをスクロールします。 最後にロックされる数値は、ハッシュ関数の値です。 そのような関数の例はMD5、SHA-1、GOST_P_34.11-94です。



独自のハッシュ関数を発明しないで、標準実装を使用してください(たとえば、Pythonの場合):



import hashlib print hashlib.sha1("Hello, Bob!").hexdigest()
      
      





: 88192e3e2e83243887410897efd90287b8e453a7







ハッシュ関数の考え方は、一方向にのみ機能するということです。戦争と平和を計算するのは非常に簡単ですが、ハッシュ関数の既に準備された値から同じ値を与えるドキュメントを見つけることはほとんど不可能です。 文書内の1文字のみを変更しても、ハッシュは完全に変更されます。



 import hashlib print hashlib.sha1("Hello, Bob?").hexdigest()
      
      





: cbad4b0e05703acf2e8572be7438830fe7f8ddf5









この点に関して、アリスがボブに送信するメッセージの整合性を制御するためにハッシュ関数を使用することは当然の希望です。アリスは各メッセージのSHA-1値を計算し、エンベロープに入れます。 テキストのSHA-1を独自に計算したボブは、結果をAlisinと比較し、途中でメッセージが変更されていないことを確認できます。



しかし、アリスとボブの間のどこかにいるマロリーは、彼らの通信を傍受して封筒を開くことを忘れていました! 彼はメッセージを変更し、SHA-1を計算して手紙に添付することができます。 ボブは値を比較しますが、何も気付かないでしょう。



認証



考えた後、アリスとボブは、SHA-1を計算するときに、「シークレット」などのテキストに一時的に秘密の単語を追加することに同意します(もちろん、実際にはアリスとボブは見つけるのが難しくなるように長い単語を使用することにしました) 。 Melloryはこの単語を知らないため、メッセージを変更しても、ハッシュを調整することはできませんよね?



例:



 import hashlib print hashlib.sha1("Secret" + "Hello, Bob").hexdigest()
      
      





: 99beeff3ef1971d2cb1be129f986739f6bcba8cc







残念ながら、問題があります。 はい、Melloryはメッセージの本文を変更することはできませんが、(彼は現在のテキストからハッシュを知っているため) 「PSの最後に常に追加することができます。実際、これはすべてナンセンスです。ボブから離れる必要があります」コンビネーションロック)。



これを防ぐために、機能を少し複雑にします。



 print hashlib.sha1("Secret" + hashlib.sha1("Hello, Bob").hexdigest()).hexdigest()
      
      





: 3f51e9fc540676bc3ce54367fd3e467f3299c743







メッセージの最後に何かを追加すると、SHA-1への「外部」呼び出しのソースデータが完全に変更され、Melloryはゲームから除外されます。



アリスとボブは、HMAC(またはハッシュベースのメッセージ認証コード )と呼ばれるもの、つまりハッシュベースのメッセージ認証コードを思いつきました。 実際には、RFC2104規格として採用されているHMACは、キーの長さ、内部のXORのペア、「内部」ハッシュへのキーの関与により、少し複雑に見えますが、本質は変わりません。



HMAC-SHA1などの標準的な実装を使用して、独自のHMAC実装を発明しないでください。



 import hmac import hashlib print hmac.new(key="Secret", msg="Hello, Bob", digestmod=hashlib.sha1).hexdigest()
      
      





ワンタイムパスワード



ワンタイムパスワードとは何ですか? これは、キーロガーを使用したり、肩越しに覗いたり、電話回線を聞いたりするのに役に立たないパスワードです。 このパスワードは1回だけ使用されます。



このスキームはどのように実装できますか? たとえば、アリスは100個のランダムパスワードを生成し、コピーをボブに渡すことができます。 ボブが次に電話をかけるとき、彼はリスト内で最高のパスワードを口述し、アリスは彼女とそれを確認し、その後両方がそれを削除します。 次回の呼び出しでは、次のパスワードを使用し、なくなるまで続けます。 これはあまり便利ではありません:リストの保存、新しいパスワードの生成など。



このスキームをアルゴリズムの形式で実装することをお勧めします。 たとえば、パスワードは順番に番号を付けたものに、秘密の番号を掛けたものです。 アリスとボブに、暗証番号が42であることに同意させてください。 その場合、最初のパスワードは42、2番目は84、3番目は126というようになります。 アルゴリズムと秘密の番号を知らないMelloryは、次にどのパスワードを推測することはありません!



もちろん、より複雑なアルゴリズムを選択する方がアルゴリズムが優れています。 アリスはHMACをリコールし、ボブにHMAC( "Secret"、password-number)の式に従ってパスワード番号Nを読み取るように提案します。 その後、キー(この場合は「シークレット」)に同意する必要がありますが、ボブは生成するパスワードの種類(たとえば20番目)を覚えるだけで済みます。



 import hmac import hashlib print hmac.new(key="Secret", msg="20", digestmod=hashlib.sha1).hexdigest()
      
      





: 393e9efaae1a687bc2dcc257c8e9e2a61f26fe4b







ただし、ボブはそのような長いパスワードを指示するために毎回微笑むわけではありません。 彼らは、たとえば最後の6文字など、その一部のみを使用することをアリスに同意します。



しばらくの間、すべてが順調に進んでいます。 ボブとアリスが使用するパスワードの種類を数えることにうんざりするまで。 HMAC()の引数として、番号の代わりに、アリスとボブが同時にアクセスできるすべてのものを使用できると誰かに伝えます...例えば、現在の時間です!



私たちのヒーローは時計を同期し、Unix時間をHMAC()の引数として使用することに同意します-UNIX 時代の開始(UTC)から経過した秒数。 急いでパスワードを入力するために、彼らは時間を30秒の「ウィンドウ」に分割することにしました。 したがって、同じパスワードが30秒間有効です。 当然、パスワードを確認するアリスは30秒間パスワードを再度使用することを許可せず(それを覚えているだけです)、それにより真に「一度限り」のままにします。



これで、パスワードは次の式を使用して計算されます: HMAC( "Secret"、unix_timestamp / 30)



現在の時刻に基づいてワンタイムパスワードを受け取りまし 。 キーを持っている人だけがこれらのパスワードを生成および検証できます(上記の例では「秘密」)。 つまり、サーバーとユーザー。



ワンタイムパスワードは、他のアルゴリズムを使用して計算できることに注意してください。 主なことは、アルゴリズムと秘密が両方の当事者に知られているということです。 しかし、以来 私たちには標準があり、それについて具体的に話します



OATH、TOTP、HOTP、RFC ... WTF?



そのため、基本的なアイデアを説明しただけです。



1)HMAC、ハッシュベースのメッセージ認証コード: RFC2104

2) HOTP 、ハッシュベースのワンタイムパスワード: RFC4226

3)TOTP、時間ベースのワンタイムパスワード: RFC6238



これらのアイデアは、認証方法の標準化を目的とするオープン認証イニシアチブ (OATH)イニシアチブの基盤の1つです。



Google 2段階認証プロセス



Googleは、時間に基づいた(およびTOTP RFC 6238アルゴリズムに基づいて計算された)ワンタイムパスワードも、 Google Authenticatorアプリケーションで使用します。このアプリケーションは、iOS、Android、BlackBerryにインストールできます。 このアプリケーションは、(Googleアカウントのメインパスワードに加えて)30秒ごとにワンタイムパスワードを自動的に生成します。 これは、誰かがあなたのプライマリパスワードをsn索したり傍受したとしても、次のワンタイムパスワードなしではシステムに入ることができないことを意味します。 便利に。



注意 :Googleの2段階認証を有効または無効にする操作については、一切責任を負いません。 あなたは自分の責任でそれらを実現することに同意します。



実際、心配する必要はありません(指示があり、信頼できるコンピューターがあり、バックアップコードがあるなど)、ボタンを軽率にクリックして一生懸命やると、アカウントに完全にアクセスできなくなります。 それでも、実験する準備ができていない場合は、Gmailをタップしないでください。 携帯端末にGoogle認証システムアプリをダウンロードし、「時間ごとのキー」を手動で追加します(たとえば、「a abc def abc def abc」、または以下のQRコードをスキャンします)。



最初に、ワンタイムパスワードを作成するために使用される秘密キーを取得する必要があります。 アカウント設定でGoogle認証システムを追加するページで確認できます。QRコードの下にあります。











2段階認証が既に有効になっている場合、古いキーを見つけることができないことに注意してください。古いキーを削除して新しいキーを生成する必要があります。 難しくありません。 最も重要なことは、使用する場合は、Google認証システムのキーをすぐに更新することを忘れないでください。



キーはBase32でエンコードされます (便宜上、スペースを削除して大文字を変換します)。



現在のワンタイムパスワードを計算するプログラム:



 import time import hmac import hashlib import base64 ### TOTP-key (get it from Google) secret = base64.b32decode("AABCDEFABCDEFABC") ### Calc counter from UNIX time (see RFC6238) counter = long(time.time() / 30) ### Use counter as 8 byte array bytes=bytearray() for i in reversed(range(0, 8)): bytes.insert(0, counter & 0xff) counter >>= 8 ### Calculate HMAC-SHA1(secret, counter) hs = bytearray(hmac.new(secret, bytes, hashlib.sha1).digest()) ### Truncate result (see RFC4226) n = hs[-1] & 0xF result = (hs[n] << 24 | hs[n+1] << 16 | hs[n+2] << 8 | hs[n+3]) & 0x7fffffff ### Print last 6 digits print str(result)[-6:]
      
      





これで、実行中のGoogle認証システムを次に配置して、値を比較できます。



upd: JavaScriptの実装例( roman_proに感謝)



upd#2:dropboxからの2段階認証を使用する場合は、シークレットの最後に「======」を追加します(これはパディングとbase32デコーダーが正しく機能するために必要です)。



結論



  1. ワンタイムパスワードは簡単です。
  2. 標準は良いです。
  3. 必要に応じて、アプリケーションに埋め込むことができます(同じGoogle認証システムのソースコードなどの既製のライブラリを使用します)。



All Articles