באופן עקרוני רנדומליות (ובשפת הקודש: אקראיות) היא דבר שקצת זר באלגוריתמים. כשאנו כותבים פונקציה או אלגוריתם אנו מצפים שהפלט יהיה זהה לאותו קלט. כלומר פונקציה דטרמניסטית. אותו קלט? אותו פלט. אבל לפעמים אנחנו כן צריכים אקראיות. לפעמים זה משהו חסר משמעות כמו למשל משהו שקשור ל-UI – כמו הדמיה של טיפות גשם או משחקים. במקרה הזה כל מתכנת שמכיר מספיק ג'אווהסקריפט ידע ש: Math.random יתן מספר רנדומלי.
const randomNumber = Math.random();
console.log(randomNumber);
המספר שניתן הוא שבר ועכשיו אפשר להכפיל ולעגל אותו בהתאם לצורך. למשל, אם אני רוצה מספר בין 1 ל-100. אני אכפיל ב-100 ואעגל למעלה.
const randomNumber = Math.ceil((Math.random() * 100));
console.log(randomNumber);
אבל לפעמים אנו צריכים מספר רנדומלי לא לצרכי משחקים ושעשועים אלא לצרכים יותר רציניים. כמו למשל צרכים קריפטוגרפיים. איזה צרכים? למשל client nonce אם אנו בסביבת צד לקוח או צרכים אחרים אם אנחנו בסביבת Node.js. אני לא אכתוב כרגע על סביבת שרת אלא על סביבת לקוח.
כשאנחנו צריכים מספר רנדומלי לצרכים האלו אנו לא משתמשים ב-Math.random. בדיוק כמו שלא משתמשים במפתח של חביתוש בלי רשות של רגע ודודלי. למה? כי האקראיות הזו היא לא אקראיות אמיתית. אני אנסה להסביר, אבל למי שאין כוח לקצת תיאוריה יכול לדלג ל" "ובת'כלס".
ג'אווהסקריפט בנויה לפי התקן של ECMAScript. והתקן די כללי בנוגע לאקראיות הזו ובגדול קובע שמי שאחראי לרנדומליות זה הדפדפן. בעבר המנועים של הדפדפנים מימשו את האקראיות בכל מיני דרכים. לפני כמה שנים, ב-2015, רוב הדפדפנים התפקסו על שימוש באלגוריתם שנקרא Xorshift128. מדובר באלגוריתם שהוא לא רנדומלי אלא מדמה רנדומליות. בשפה של הקריפטוגרפים זה אלגוריתם מסוג PRNG שזה ראשי תבות של pseudo random number generator. מה זה אומר? שמדובר באלגוריתם שמייצר רנדומליות אבל יש כמה בעיות ברנדומליות הזו, מבלי להכנס לענייני קריפטגורפיה – זה אומר שבתנאים מסוימים אפשר לנחש את המספרים הרנדומליים האלו. כי הם לא מספיק רנדומליים. בדיוק בשביל זה אנחנו צריכים להשתמש באלגוריתמים מסוג CSPRNG שזה ראשי תבות של Cryptographically-secure pseudorandom number generator. שזה אומר אלגוריתמים שנותנים אקראיות שמספיקה לשימושים קריפטוגרפיים שדורשים מספרים רנדומליים. Math.random הוא מהסוג הראשון והוא לא טוב לצרכים קריפטוגרפיים כמו nonce או כל צורך אחר שקשור לצרכי הצפנה או אבטחת מידע. אם אתם נדרשים על ידי השרת לספק איזשהו מפתח רנדומלי, אתם צריכים להשתמש ב-CSPRNG ואת זה מקבלים במקום אחר.
ובת'כלס?
בת'כלס בכל פעם שאתם נדרשים למספר רנדומלי לצרכי אבטחה בצד הלקוח – כדאי להשתמש בספרית web crypto. וספציפית ב-Crypto.getRandomValues – פונקציה שמחזירה ערכים רנדומליים שבטוחים לשימוש קריפטוגרפית. לא ליצירת מפתחות אבל בכל פעם שנדרש ערך רנדומלי – ואם אתם מפתחי צד לקוח – ברגע שמבקשים מכם nonce עבור השרת מסיבה מסוימת – אתם צריכים להשתמש ב-Crypto.getRandomValues.
הפונקציה עובדת בצורה מעט שונה. היא מקבלת TypedArray (שזה מערך מסוג מסוים) ומאכלסת אותו במספרים רנדומליים. למשל – כאן אני יוצר מערך מסוג Uint32Array באורך של איבר אחד ומאכלס אותו:
const randomNumbersArray = new Uint32Array(1);
globalThis.crypto.getRandomValues(randomNumbersArray);
console.log(randomNumbersArray[0]);
סוג המערך מכתיב כמובן את גודל המספר ואת העובדה אם הוא שלילי הוא חיובי. אם אנו רוצים מספר יותר קטן אפשר להשתמש ב:
const randomNumbersArray = new Uint8Array(1);
globalThis.crypto.getRandomValues(randomNumbersArray);
console.log(randomNumbersArray[0]);
אם typedArray הוא מוזר וחדש עבורכם, אז אולי כדאי לקרוא עליו ב-MDN. אבל בגדול זה מערך שיכול להכנס אליו נתונים מסוג מסוים של מספרים. אם globalThis הוא חדש לכם – אז כאן כתבתי עליו כמה מילים.
אבל ככה מייצרים קוד טוב שמייצר אקראיות מספיק טובה לשימושים קריפטגורפיים שנדרשים בצד הלקוח. שימוש ב-API שנועד לצרכים קריפטוגרפיים כאשר יש צורך כזה מבטיח שהקוד שלכם יישאר עמיד. כי גם אם (למשל), יחליפו את האלגוריתם מאחורי getRandomValues – הקוד שלכם ימשיך לעבוד ויהיה מספיק בטוח. אז אני ממליץ מאוד להשתמש בו ובכלל להכיר את crypto בצד הלקוח 🙂
2 תגובות
לכתבה קצת יותר תיאורטית אפשר לעיין גם כאן
https://blog.chv.ovh/true-random/
עזרת לי ממש תודה רבה!