當我們可以只執行 Math.random() 時,為什麼還要使用 OTP 函式庫
一次性密碼 (OTP) 廣泛用於各種應用程式和服務中的身份驗證和驗證目的。伺服器通常會產生它們並透過簡訊、電子郵件或其他管道將它們發送給使用者。然後,使用者輸入 OTP 來確認其身分或執行操作。
我接到一個任務,必須在 Node JS 中實作基於 OTP 的驗證。在整合這樣的東西之前,我確信我們大多數開發人員/工程師都會在互聯網上尋找最佳實踐、教程、最新技術趨勢以及其他主要軟體系統在實現過程中面臨的問題。於是我就這麼做了,最吸引我注意的是像 otp-lib 和 otp-generator 這樣的函式庫,它們唯一的功能就是產生 OTP。其餘任務(例如透過簡訊或電子郵件發送)仍然需要透過其他方式完成。在知道這類庫存後,我想到的第一個問題是,為什麼我們必須花這麼大的篇幅來使用函式庫來產生 OTP,而我們所要做的只是寫一行程式碼:
const otp = Math.ceil(Math.random() * 10000)
在這篇文章中,我將解釋我在OTP 產生器的小型研究中學到的東西,為什麼使用Math.random() 產生OTP 是一個壞主意,產生OTP 的其他方法是什麼,以及為什麼需要一個庫應該用於這樣的任務嗎?
隨機數的類型
隨機數主要有兩種:
- 偽隨機數 (PRN)
- 加密隨機數 (CRN)。
偽隨機數
偽隨機數是由一種演算法產生的,該演算法採用稱為種子的初始值,並產生看似隨機的數字序列。但是,該演算法是確定性的,這意味著如果您知道種子和演算法,則可以預測序列中的下一個數字。 Javascript 的 Math.random() 和 Python 的 random.randInt() 是偽隨機數產生器的範例。
加密隨機數
加密隨機數是由不可預測且無法複製或猜測的過程產生的。它們通常基於一些物理現象,例如大氣噪聲、熱噪聲或量子效應。
Math.random() 是如何運作的?
不同的 Javascript 引擎在產生隨機數時的行為略有不同,但這一切本質上都歸結為單一演算法 XorShift128+。
XorShift 是一種確定性演算法,它使用加法作為更快的非線性變換解決方案。與使用乘法的同類演算法相比,此演算法速度更快。它比 Mersenne Twister(Python 的 random 模組使用)失敗的可能性更小
演算法接受兩個狀態變量,對它們應用一些 XOR 和移位,並傳回更新後的狀態變數的總和,這是一個整數。這些狀態通常使用系統時鐘進行播種,因為這是唯一數字的良好來源。
異或移位加上 JavaScript 的實作如下圖:
let state0 = 1; let state1 = 2; function xorShiftPlus() { let s1 = state0; let s0 = state1; state0 = s0; s1 ^= s1 << 23; s1 ^= s1 >> 17; s1 ^= s0; s1 ^= s0 >> 26; state1 = s1; return state0 + state1; }
使用常數的 OR 運算將傳回的整數轉換為雙精確度值。詳細實作可以在chrome原始碼中找到。
如何預測 Math.random() 產生的隨機數
預測 Math.random() 的結果很困難,但也不是完全不可能。了解了演算法,如果知道 state0 和 state1 的值,就可以輕鬆地重新產生相同的隨機數。
逆向工程 XorShift128+ 使用 Z3 定理證明器,您可以透過提供伺服器產生的 3 個連續隨機數來找到 state0 和 state1 的值。
Z3 求解器的實作可以在這裡找到。
現在的問題是如何從伺服器取得這 3 個隨機數。這是最困難的部分,可以在以下某些情況下獲得:
- If an API returns a randomly generated number in its response or headers, it can easily be obtained by sending requests at set intervals.
- API documentation like OpenAPI/Swagger in modern applications is generated on the server. Sometimes their responses can contain an example value that uses a random number.
- With frameworks like NextJS that use server-side rendering while also being capable of handling backend API integrations, there are high chances of getting randomly generated numbers from the content served by them.
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.
Generating a Cryptographic Random Number in NodeJS
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:
- Storing OTPs in the database creates a lot of garbage data that has to be cleaned up periodically. OTP means a one-time password that can expire after a single use. It can also expire if not used for a specific duration or a new OTP is requested without using the previous one. This mainly adds unnecessary overhead to the database operations for maintaining valid OTPs while also consuming storage space.
- Storing OTPs in the database poses a security risk if the database is compromised. An attacker who gains access to the database can read the OTPs and use them to bypass the authentication or verification process. This can lead to account takeover, identity theft, or fraud.
- Storing OTPs in the database makes them vulnerable to replay attacks. A replay attack is when an attacker intercepts an incoming valid OTP and uses it again before it expires. This can allow the attacker to perform unauthorised actions or access sensitive information.
What do the OTP libraries do differently?
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
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 基本上是一個編程的硬體密鑰,您可以將其物理連接到電腦或手機。您無需透過簡訊或電子郵件接收代碼,只需按 Yubikey 上的按鈕即可驗證和驗證自己的身份。
HOTP的優點是:
• 它不需要將 OTP 儲存在資料庫中,因為它可以即時產生和驗證。
• 它不依賴偽隨機數,因為它使用不可預測且不可逆的加密雜湊函數。
• 它可以抵抗重播攻擊,因為每個 OTP 只有效一次。
HOTP 的缺點是:
• 它需要伺服器和使用者計數器之間的同步。如果因網路延遲、傳輸錯誤或裝置遺失而導致驗證失敗。
• 只要不使用新產生的 HOTP,它就保持有效,這可能是個漏洞。
• 需要一種安全的方式來分發和儲存金鑰。如果金鑰洩漏或被盜,OTP 可能會被洩露。
托普
TOTP 代表基於時間的一次性密碼。它是一種基於金鑰、時間戳記和紀元產生 OTP 的演算法。
- 金鑰是伺服器和使用者之間共享的隨機字串。可以透過產生 SHA1( "secretvalue" + user_id ) 為每個使用者唯一建立它。
- 時間戳記是整數,表示當前時間(以秒為單位)
- 紀元是演算法產生相同結果的持續時間。一般維持在30秒-1分鐘之間。
演算法的工作原理如下:
• 伺服器為使用者決定金鑰並透過身份驗證器應用程式等媒介共用。
• 伺服器可以直接產生 OTP 並透過郵件或 SMS 傳送給用戶,也可以要求使用者使用身份驗證器使用共用金鑰產生 OTP。
• 使用者可以直接發送透過郵件或簡訊收到的 OTP,也可以在驗證器應用程式中產生它,以防在固定時間視窗內進行 2FA。
• 伺服器將 OTP 與自己產生的 OTP 進行比較,並驗證它們在紀元時間範圍內是否足夠接近。
TOTP的優點是:
• 它不需要將 OTP 儲存在資料庫中,因為它可以即時產生和驗證。
• 它不依賴偽隨機數,因為它使用不可預測且不可逆的加密雜湊函數。
• 它可以抵抗重播攻擊,因為每個 OTP 僅在短時間內有效。
• 它不需要伺服器和使用者時間戳記之間的同步。只要他們有相當準確的時鐘,他們就可以獨立產生和驗證 OTP。
TOTP的缺點是:
• 它需要一種安全的方式來分發和儲存金鑰。如果金鑰洩漏或被盜,OTP 可能會被洩露。
• 它需要伺服器和使用者都有可靠的時間來源。如果他們的時鐘有偏差或被竄改,驗證就會失敗。
• 伺服器必須考慮處理請求時的時間漂移或延遲,因此它應該保持比客戶端稍大的紀元。
結論
透過我們對 OTP 的一點研究之旅,我們了解到 Math.random() 是可以預測、利用和重播的。我們也了解到,將 OTP 儲存在資料庫中並不是一個好的做法。
TOTP可以產生安全且高效的OTP,並且還可以驗證它們。它可以離線和線上產生 OTP,不需要同步或存儲,並且可以抵抗重播攻擊。因此,它解決了我們與最佳實踐、安全性和可靠性相關的大部分問題。
以上是當我們可以只執行 Math.random() 時,為什麼還要使用 OTP 函式庫的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

從C/C 轉向JavaScript需要適應動態類型、垃圾回收和異步編程等特點。 1)C/C 是靜態類型語言,需手動管理內存,而JavaScript是動態類型,垃圾回收自動處理。 2)C/C 需編譯成機器碼,JavaScript則為解釋型語言。 3)JavaScript引入閉包、原型鍊和Promise等概念,增強了靈活性和異步編程能力。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務
