ワンタイム パスワード (OTP) は、さまざまなアプリケーションやサービスで認証と検証の目的で広く使用されています。通常、サーバーがそれらを生成し、SMS、電子メール、またはその他のチャネル経由でユーザーに送信します。次に、ユーザーは OTP を入力して身元を確認するか、アクションを実行します。
Node JS で OTP ベースの検証を実装する必要があるタスクを受け取りました。このようなものを統合する前に、私たちのほとんどの開発者/エンジニアは、ベスト プラクティス、チュートリアル、最近の技術トレンド、他の主要なソフトウェア システムが実装中に直面する問題などをインターネットで調べていると思います。そこで私はそれを実行しましたが、最も注目を集めたのは、OTP を生成することだけが機能する otp-lib や otp-generator のようなライブラリでした。 SMS や電子メールでの送信などの残りのタスクは、引き続き他の手段で行う必要があります。このようなライブラリが存在することを知った後に頭に浮かぶ最初の疑問は、ワンライナーを書くだけで済むのに、なぜわざわざライブラリを使用して OTP を生成する必要があるのかということです:
このブログ投稿では、OTP ジェネレーターの小規模な研究中に学んだこと、OTP の生成に Math.random() を使用するのがなぜ悪い考えなのか、OTP を生成する他の方法は何なのか、なぜこのようなタスクにはライブラリを使用する必要がありますか?
乱数には主に 2 種類があります:
擬似乱数は、シードと呼ばれる初期値を取得し、ランダムに見える一連の数値を生成するアルゴリズムによって生成されます。ただし、アルゴリズムは決定論的です。つまり、シードとアルゴリズムを知っていれば、シーケンス内の次の数値を予測できます。 Javascript の Math.random() と Python のrandom.randInt() は、擬似乱数生成器の例です。
暗号乱数は、予測不可能なプロセスによって生成され、再現したり推測したりすることはできません。これらは通常、大気騒音、熱雑音、量子効果などの物理現象に基づいています。
Javascript エンジンが異なると、乱数を生成する際の動作が少し異なりますが、基本的にはすべて単一のアルゴリズム XorShift128+ によって決まります。
XorShift は、高速な非線形変換ソリューションとして加算を使用する決定論的アルゴリズムです。乗算を使用する他のアルゴリズムと比較して、このアルゴリズムは高速です。また、メルセンヌ ツイスター (Python のランダム モジュールで使用される) よりも失敗する可能性が低くなります
このアルゴリズムは 2 つの状態変数を受け取り、XOR とシフトを適用して、更新された状態変数の合計を整数で返します。通常、状態はシステム クロックを使用してシードされます。これは、一意の番号の優れたソースであるためです。
JavaScript での XOR シフト プラスの実装は次のようになります:
返された整数は、定数との OR 演算を使用して double に変換されます。詳細な実装は、Chrome ソース コードで確認できます。
Math.random() の結果を予測するのは困難ですが、完全に不可能というわけではありません。アルゴリズムを理解していれば、state0 と state1 の値がわかっていれば、同じ乱数を簡単に再生成できます。
リバース エンジニアリング XorShift128+ Z3 定理証明器を使用すると、サーバーによって生成された 3 つの連続する乱数を提供することで、state0 と state1 の値を見つけることができます。
Z3 ソルバーの実装はここにあります。
ここで問題は、サーバーからこれら 3 つの乱数を取得する方法です。それが難しい部分ですが、次のような場合に取得できます:
Another approach to exploit a random number is using the fact that Math.random() only returns numbers between 0 and 1 with 16 decimal places. This means that there are only 10^16 possible values that Math.random() can return. This is a very small space compared to the space of possible OTPs. if your OTP has 6 digits, there are 10^6 possible values. This visualizer shows that there is a pattern to the numbers generated. Using it, the possibilities can be reduced by 30%. Therefore, if you can guess or brute-force some of the digits of the OTP, you can reduce the space of possible values and increase your chances of finding the correct OTP.
As mentioned previously, cryptographic random numbers are non-deterministic because they depend on the physical factors of a system. Every programming language can access those factors using low-level OS kernel calls.
NodeJS provides its inbuilt crypto module, which we can use to generate randomBytes and then convert them to a number. These random bytes are cryptographic and purely random in nature. The generated number can easily be truncated to the exact number of digits we want in OTP.
import * as crypto from 'crypto'; const num = parseInt(crypto.randomBytes(3).toString('hex'), 16) // num.toString().slice(0,4) // truncate to 4 digits
NodeJS 14.10+ provides another function from crypto to generate a random number in a given min-max range.
crypto.randomInt(1001, 9999)
Even after knowing the vulnerability of Math.random() and finding a more secure way to generate a random number cryptographically, we still remain with the same question from the beginning. Why do we have to go to such lengths to use a library to generate OTP when all we have to do is write a one-liner?
Before answering this questions, let's take a look at what is the inconvenience faced while handling and storing an OTP. The problem with using the above method to generate OTPs is that you have to store them in the database in order to verify them later. Storing the OTP in the database is not a good practice for the following reasons:
The OTP libraries use different algorithms and techniques to generate and verify OTPs that behave similarly to a Cryptographic random OTP, while also removing the overhead to store the OTP in a database.
There are mainly two types of OTP implementation techniques.
HOTP stands for HMAC-based One-Time Password. It is an algorithm that generates an OTP based on a secret key and a counter. The secret key is a random string that is shared between the server and the user. The counter is an integer that increments every time an OTP is generated or verified.
The algorithm works as follows:
• The server and the user generate the same OTP by applying a cryptographic hash function, such as SHA-1, to the concatenation of the secret key and the counter.
• The server and the user truncate the hash value to obtain a fixed-length OTP, usually 6 or 8 digits.
• The user sends the OTP to the server for verification.
• The server compares the OTP with its own generated OTP and verifies it if they match.
• The server and the user increment their counters by one.
HOTP は、Yubikey のようなハードウェア トークン ベースの認証で主に使用されます。 Yubikey は基本的に、コンピュータまたは電話に物理的に接続できるプログラムされたハードウェア キーです。 SMS や電子メールからコードを受け取る代わりに、Yubikey のボタンを押すだけで自分自身を確認して認証できます。
• OTP はその場で生成および検証できるため、データベースに保存する必要はありません。
• 予測不可能かつ不可逆的な暗号化ハッシュ関数を使用するため、疑似乱数に依存しません。
• 各 OTP は 1 回だけ有効であるため、リプレイ攻撃に耐性があります。
• サーバーとユーザーのカウンター間の同期が必要です。ネットワークの遅延、送信エラー、デバイスの紛失などが原因で同期が取れていない場合、検証は失敗します。
• 新たに生成された HOTP が使用されない限り有効ですが、脆弱性となる可能性があります。
• 秘密鍵を配布および保管するには安全な方法が必要です。秘密キーが漏洩または盗難された場合、OTP が侵害される可能性があります。
TOTP は、タイムベースのワンタイム パスワードの略です。秘密鍵、タイムスタンプ、エポックに基づいて OTP を生成するアルゴリズムです。
アルゴリズムは次のように機能します:
• サーバーはユーザーの秘密キーを決定し、Authenticator アプリなどの媒体を介してそれを共有します。
• サーバーは OTP を直接生成し、メールまたは SMS でユーザーに送信することも、オーセンティケーターを使用して共有キーを使用して OTP を生成するようにユーザーに要求することもできます。
• ユーザーは、メールまたは SMS で受信した OTP を直接送信することも、2FA の場合は一定の時間枠で認証アプリで OTP を生成することもできます。
• サーバーは OTP を独自に生成した OTP と比較し、それらがエポック時間範囲内で十分に近いかどうかを検証します。
• OTP はその場で生成および検証できるため、データベースに保存する必要はありません。
• 予測不可能かつ不可逆的な暗号化ハッシュ関数を使用するため、疑似乱数に依存しません。
• 各 OTP は短期間のみ有効であるため、リプレイ攻撃に耐性があります。
• サーバーとユーザーのタイムスタンプ間の同期は必要ありません。かなり正確なクロックを持っている限り、OTP を独立して生成および検証できます。
• 秘密鍵を配布および保存するには安全な方法が必要です。秘密キーが漏洩または盗難された場合、OTP が侵害される可能性があります。
• サーバーとユーザーの両方にとって信頼できる時間源が必要です。時計が歪んでいたり改ざんされている場合、検証は失敗します。
• サーバーはリクエストの処理における時間のずれや遅延を考慮する必要があるため、クライアントよりもわずかに長いエポックを維持する必要があります。
OTP に関するちょっとした調査を通じて、私たちは Math.random() が予測、悪用、再生できることを知りました。また、OTP をデータベースに保存するのは良い習慣ではないこともわかりました。
TOTP は安全で効率的な OTP を生成し、検証することもできます。オンラインだけでなくオフラインでも OTP を生成でき、同期やストレージを必要とせず、リプレイ攻撃に耐性があります。したがって、ベストプラクティス、セキュリティ、信頼性に関する懸念のほとんどが解決されます。
以上がMath.random() を実行するだけで済むのに、なぜ OTP ライブラリを使用するのでしょうか。の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。