Kata laluan sekali sahaja (OTP) digunakan secara meluas untuk tujuan pengesahan dan pengesahan dalam pelbagai aplikasi dan perkhidmatan. Pelayan biasanya menjananya dan menghantarnya kepada pengguna melalui SMS, e-mel atau saluran lain. Pengguna kemudiannya memasukkan OTP untuk mengesahkan identiti mereka atau melakukan tindakan.
Saya mendapat tugas di mana kami perlu melaksanakan pengesahan berasaskan OTP dalam Node JS. Sebelum menyepadukan sesuatu seperti ini, saya pasti kebanyakan kita pembangun/jurutera melihat di Internet untuk amalan terbaik, tutorial, arah aliran teknikal terkini dan masalah sistem perisian utama lain yang dihadapi semasa pelaksanaannya. Jadi saya melakukannya, dan perkara yang paling menarik perhatian saya ialah perpustakaan seperti otp-lib, dan otp-generator, yang hanya berfungsi untuk menjana OTP. Selebihnya tugas seperti menghantarnya melalui SMS atau e-mel masih perlu dilakukan dengan cara lain. Soalan pertama yang terlintas di fikiran selepas mengetahui bahawa perpustakaan sebegitu wujud ialah mengapa kita perlu bersusah payah menggunakan perpustakaan untuk menjana OTP sedangkan kita hanya perlu menulis satu baris:
const otp = Math.ceil(Math.random() * 10000)
Dalam catatan blog ini, saya akan menerangkan perkara yang saya pelajari semasa penyelidikan kecil kami untuk penjana OTP, mengapa menggunakan Math.random() untuk menjana OTP adalah idea yang tidak baik, apakah cara lain untuk menjana OTP dan mengapa perpustakaan patut digunakan untuk tugasan sedemikian?
Terdapat dua jenis nombor rawak terutamanya:
Nombor pseudo rawak dijana oleh algoritma yang mengambil nilai awal, dipanggil benih dan menghasilkan urutan nombor yang kelihatan rawak. Walau bagaimanapun, algoritma adalah deterministik, bermakna jika anda mengetahui benih dan algoritma, anda boleh meramalkan nombor seterusnya dalam jujukan. Javascript's Math.random() dan Python's random.randInt() ialah contoh penjana nombor rawak pseudo.
Nombor rawak kriptografi dijana oleh proses yang tidak dapat diramalkan dan tidak boleh dihasilkan semula atau diteka. Ia biasanya berdasarkan beberapa fenomena fizikal, seperti hingar atmosfera, hingar haba atau kesan kuantum.
Enjin Javascript yang berbeza berkelakuan sedikit berbeza apabila menjana nombor rawak, tetapi semuanya pada asasnya bergantung kepada algoritma tunggal XorShift128+.
XorShift ialah algoritma deterministik, yang menggunakan penambahan sebagai penyelesaian transformasi bukan linear yang lebih pantas. Berbanding dengan rakan sebayanya, yang menggunakan pendaraban, algoritma ini lebih pantas. Ia juga mempunyai peluang kegagalan yang lebih kecil daripada Mersenne Twister (digunakan oleh modul rawak Python)
Algoritma mengambil dalam dua pembolehubah keadaan, menggunakan beberapa XOR dan beralih pada mereka, dan mengembalikan jumlah pembolehubah keadaan yang dikemas kini iaitu Integer. Negeri-negeri tersebut secara amnya disemai menggunakan jam sistem kerana ia merupakan sumber yang baik untuk nombor unik.
Pelaksanaan anjakan XOR tambah dalam javascript kelihatan seperti ini:
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; }
Integer yang dikembalikan ditukar kepada dua kali menggunakan operasi ATAU dengan pemalar. Anda boleh mendapatkan pelaksanaan terperinci pada kod sumber chrome.
Meramalkan keputusan Math.random() adalah sukar, namun, ia bukanlah mustahil sepenuhnya. Mengetahui algoritma, anda boleh menjana semula nombor rawak yang sama dengan mudah jika anda mengetahui nilai state0 dan state1.
Kejuruteraan songsang XorShift128+ Menggunakan prover teorem Z3 anda boleh mencari nilai state0 dan state1 dengan menyediakan 3 nombor rawak berturut-turut yang dijana oleh pelayan.
Pelaksanaan penyelesai Z3 boleh didapati di sini.
Sekarang persoalannya timbul bagaimana untuk mendapatkan 3 nombor rawak tersebut daripada pelayan. Itulah bahagian yang sukar, dan boleh didapati dalam beberapa kes berikut:
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 kebanyakannya digunakan dalam pengesahan berasaskan token perkakasan seperti Yubikey. Yubikey pada asasnya ialah kunci perkakasan yang diprogramkan yang boleh anda sambungkan secara fizikal ke komputer atau telefon anda. Daripada menerima kod daripada SMS atau e-mel, anda hanya boleh menekan butang pada Yubikey untuk mengesahkan dan mengesahkan diri anda.
• Ia tidak memerlukan penyimpanan OTP dalam pangkalan data, kerana ia boleh dijana dan disahkan dengan cepat.
• Ia tidak bergantung pada nombor pseudo-rawak, kerana ia menggunakan fungsi cincang kriptografi yang tidak dapat diramalkan dan tidak dapat dipulihkan.
• Ia tahan untuk memainkan semula serangan, kerana setiap OTP sah sekali sahaja.
• Ia memerlukan penyegerakan antara pelayan dan kaunter pengguna. Jika ia tidak segerak, disebabkan oleh kelewatan rangkaian, ralat penghantaran atau kehilangan peranti, pengesahan akan gagal.
• Ia kekal sah selagi HOTP yang baru dijana tidak digunakan, itu boleh menjadi kelemahan.
• Ia memerlukan cara selamat untuk mengedar dan menyimpan kunci rahsia. Jika kunci rahsia dibocorkan atau dicuri, OTP boleh dikompromi.
TOTP adalah singkatan kepada Kata Laluan Satu Masa Berasaskan Masa. Ia ialah algoritma yang menjana OTP berdasarkan kunci rahsia, cap masa dan zaman.
Algoritma berfungsi seperti berikut:
• Pelayan memutuskan kunci rahsia untuk pengguna dan berkongsinya melalui medium seperti apl Pengesah.
• Pelayan boleh terus menjana OTP dan menghantarnya kepada pengguna melalui mel atau SMS, atau ia boleh meminta pengguna menggunakan Pengesah untuk menjana OTP menggunakan kunci kongsi.
• Pengguna boleh terus menghantar OTP yang diterima melalui mel atau SMS atau boleh menjananya dalam apl pengesah sekiranya berlaku 2FA dalam tetingkap masa tetap.
• Pelayan membandingkan OTP dengan OTP yang dijana sendiri dan mengesahkannya jika ia cukup dekat dalam julat masa zaman.
• Ia tidak memerlukan penyimpanan OTP dalam pangkalan data, kerana ia boleh dijana dan disahkan dengan cepat.
• Ia tidak bergantung pada nombor pseudo-rawak, kerana ia menggunakan fungsi cincang kriptografi yang tidak dapat diramalkan dan tidak dapat dipulihkan.
• Ia tahan terhadap serangan ulangan, kerana setiap OTP hanya sah untuk tempoh masa yang singkat.
• Ia tidak memerlukan penyegerakan antara pelayan dan cap masa pengguna. Selagi mereka mempunyai jam yang agak tepat, mereka boleh menjana dan mengesahkan OTP secara bebas.
• Ia memerlukan cara yang selamat untuk mengedar dan menyimpan kunci rahsia. Jika kunci rahsia dibocorkan atau dicuri, OTP boleh dikompromi.
• Ia memerlukan sumber masa yang boleh dipercayai untuk kedua-dua pelayan dan pengguna. Jika jam mereka diserong atau diusik, pengesahan akan gagal.
• Pelayan perlu mempertimbangkan masa hanyut atau kelewatan dalam memproses permintaan, jadi ia harus mengekalkan zaman yang lebih besar sedikit daripada pelanggan.
Melalui perjalanan penyelidikan kecil kami tentang OTP, kami mendapat tahu bahawa Math.random() boleh diramal, dieksploitasi dan dimainkan semula. Kami juga mendapat tahu bahawa menyimpan OTP dalam pangkalan data bukanlah amalan yang baik.
TOTP boleh menjana OTP yang selamat dan cekap, dan juga boleh mengesahkannya. Ia boleh menjana OTP luar talian dan juga dalam talian, tidak memerlukan penyegerakan atau storan, dan tahan terhadap serangan ulangan. Oleh itu, ia menyelesaikan kebanyakan kebimbangan kami berkaitan amalan terbaik, keselamatan dan kebolehpercayaan.
Atas ialah kandungan terperinci Mengapa kita menggunakan perpustakaan OTP sedangkan kita hanya boleh melakukan Math.random(). Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!