1. ホーム
  2. python

[解決済み] PythonによるGoogle Authenticatorの実装

2022-09-24 09:08:22

質問

ワンタイムパスワードを使用しようとしています。 Google Authenticatorアプリケーション .

Google Authenticatorが行うこと

基本的に、Google Authenticatorは2種類のパスワードを実装しています。

  • ホットプ - HMACベースのワンタイムパスワードで、呼び出しごとにパスワードが変更されることを意味し、準拠するのは RFC4226 に準拠しています。
  • TOTP - 時間ベースのワンタイムパスワードで、30秒周期で変化する(私の知る限り)。

Google Authenticatorはオープンソースとしてこちらでも公開されています。 code.google.com/p/google-authenticator

現在のコード

私は HOTP および TOTP パスワードを生成するための既存のソリューションを探しましたが、あまり見つかりませんでした。私が持っているコードは、HOTP を生成する責任を負う次のスニペットです。

import hmac, base64, struct, hashlib, time

def get_token(secret, digest_mode=hashlib.sha1, intervals_no=None):
    if intervals_no == None:
        intervals_no = int(time.time()) // 30
    key = base64.b32decode(secret)
    msg = struct.pack(">Q", intervals_no)
    h = hmac.new(key, msg, digest_mode).digest()
    o = ord(h[19]) & 15
    h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
    return h

問題は、上記のコードを使用して生成したパスワードが、Android 用の Google Authenticator アプリで生成したものと同じではないことです。複数の intervals_no の値を複数回試したにもかかわらず (ちょうど最初の 10000 回、最初は intervals_no = 0 で始まる)、そして secret はGAアプリ内で提供されるキーと同じです。

私が持っている質問

私の質問は

  1. 私は何を間違えているのでしょうか?
  2. PythonでHOTPやTOTPを生成するにはどうしたらよいですか?
  3. このための既存の Python ライブラリはありますか?

要約すると、私のPythonコード内にGoogle Authenticator認証を実装するのに役立つ手がかりをください。

どのように解決するのですか?

私は私の質問に報奨金を設定したかったのですが、私は解決策を作成することに成功しました。私の問題は、不正な値に関連付けられているようだ secret キーの値が正しくないことです(それは base64.b32decode() 関数の正しいパラメータでなければなりません)。

以下では、その使用方法に関する説明とともに、完全な作業ソリューションを掲載します。

コード

以下のコードで十分です。という別のモジュールとしてGitHubにアップロードしています。 onetimepass (というモジュールとしてGitHubにアップロードしました(ここから入手できます。 https://github.com/tadeck/onetimepass ).

import hmac, base64, struct, hashlib, time

def get_hotp_token(secret, intervals_no):
    key = base64.b32decode(secret, True)
    msg = struct.pack(">Q", intervals_no)
    h = hmac.new(key, msg, hashlib.sha1).digest()
    o = ord(h[19]) & 15
    h = (struct.unpack(">I", h[o:o+4])[0] & 0x7fffffff) % 1000000
    return h

def get_totp_token(secret):
    return get_hotp_token(secret, intervals_no=int(time.time())//30)

2つの機能を持つ。

  • get_hotp_token() はワンタイムトークンを生成します(一回使用したら無効になるはずです)。
  • get_totp_token() 時間ベースのトークンを生成する(30秒間隔で変更)。

パラメータ

パラメータに至っては

  • secret は、サーバー(上記のスクリプト)とクライアント(Google Authenticator、アプリケーション内でパスワードとして提供することによって)が知っている秘密の値です。
  • intervals_no はトークンを生成するたびに増加する数です (これはおそらく、過去にチェックされた最後の成功の後に有限個の整数をチェックすることで、サーバー上で解決されるはずです)。

使用方法

  1. 生成する secret (の正しいパラメータである必要があります。 base64.b32decode() ) - できれば16文字で (ただし = 記号は使用しないでください)、スクリプトとGoogle Authenticatorの両方で確実に動作しました。
  2. 使用方法 get_hotp_token() を使用することで、ワンタイムパスワードを使用後に無効にすることができます。Google Authenticator では、このタイプのパスワードはカウンタに基づくとされています。サーバ側でこれを確認するには、いくつかの intervals_no の値をいくつかチェックする必要があります (何らかの理由でユーザがリクエストの間にパスワードを生成していないことを保証できないため)。 intervals_no の値より小さくはありません (したがって、おそらくどこかに保存する必要があります)。
  3. 使用する get_totp_token() を使います。両方のシステムが正しい時刻を設定していることを確認する必要があります (つまり、両方のシステムが任意の時点で同じ Unix タイムスタンプを生成することを意味します)。
  4. ブルートフォース攻撃から身を守ることを確認します。時間ベースのパスワードが使用される場合、30 秒以内に 1000000 個の値を試すと、100% の確率でパスワードを推測することができます。HMACベースのパスワード(HOTP)の場合、さらに悪いようです。

テスト例

以下のコードをHMACベースのワンタイムパスワードに使用する場合。

secret = 'MZXW633PN5XW6MZX'
for i in xrange(1, 10):
    print i, get_hotp_token(secret, intervals_no=i)

を実行すると、次のような結果になります。

1 448400
2 656122
3 457125
4 35022
5 401553
6 581333
7 16329
8 529359
9 171710

は、Google Authenticatorアプリが生成するトークンに対応します(ただし、6文字より短い場合は、アプリが先頭にゼロを追加して6文字にします)。