קטגוריית OWASP: MASVS-CRYPTO: קריפטוגרפיה
סקירה כללית
מחולל מספרים פסאודו-אקראיים (PRNG) הוא אלגוריתם שמייצר רצפים של מספרים שניתן לחזות אותם על סמך ערך התחלתי שנקרא seed. לרצף מספרים שנוצר על ידי PRNG יש בערך את אותן תכונות כמו לרצף מספרים אקראי באמת, אבל הוא מהיר יותר ודורש פחות משאבי מחשוב כדי ליצור אותו.
במילים אחרות, ל-PRNG יש רמת אמינות גבוהה יותר מאשר ל-RNG חלש (למשל, java.math.Random) מבחינת אחידות של חלוקת האנטרופיה, שמדמה רצפים של מספרים אקראיים באמת. יצירה של מספרים אקראיים באמת דורשת ציוד מיוחד, ולרוב היא לא חלק מתהליך פיתוח רגיל. המסמך הזה לא עוסק ביצירת מספרים אקראיים באמת, אלא רק ב-PRNGs, כי זו המתודולוגיה הסטנדרטית שנמצאת בשימוש.
נקודות חולשה ב-PRNG מתרחשות כשהמפתחים משתמשים ב-PRNG רגיל למטרות קריפטוגרפיות, במקום ב-PRNG מאובטח מבחינה קריפטוגרפית (CSPRNG). ל-CSPRNG יש דרישות מחמירות יותר, וכשהערך הראשוני לא ידוע, הן צריכות לתת לתוקף רק יתרון זניח בהבחנה בין רצף פלט לבין רצף אקראי בפועל.
בנוסף, התוקפים יכולים לנחש את רצף המספרים שנוצר אם נעשה שימוש בערכי התחלה צפויים – כמו אלה שהוצפנו על ידי המפתח – כדי לאתחל PRNG או CSPRNG, כי התוקף יכול לנחש את ערך ההתחלה וכך לחזות את הפלט שנוצר על ידי ה-PRNG.
השפעה
אם משתמשים ב-PRNG לא מאובטח מבחינה קריפטוגרפית בהקשר אבטחתי כמו אימות, תוקף יכול לנחש את המספרים שנוצרו באופן אקראי ולקבל גישה לנתונים או לתכונות עם הרשאות מיוחדות.
אמצעי צמצום סיכונים
כללי
- שימוש ב-
java.security.SecureRandomכשקיימות השלכות על האבטחה - בכל מקרה אחר, משתמשים ב-
java.util.Random. - אף פעם אל תשתמשו ב-
Math.random!
java.security.SecureRandom
מומלץ לשימושים שקשורים לאבטחה. אם גרסת ליבת ה-Linux היא 5.17 ומעלה או חסימת השרשור מקובלת, צריך להמתין עד שתצטבר אנטרופיה מספקת לפני שיוצרים את המספרים האקראיים (כלומר, משתמשים ב-/dev/random). כדי לעשות זאת, צריך לקרוא ל-getInstanceStrong():
Kotlin
val rand = SecureRandom.getInstanceStrong()
Java
SecureRandom rand = SecureRandom.getInstanceStrong();
אחרת, בגרסאות של ליבת לינוקס שקודמות לגרסה 5.17, כשחסימת השרשור לא מקובלת בזמן יצירת מספרים אקראיים, צריך לקרוא ישירות לבונה SecureRandom:
Kotlin
import java.security.SecureRandom
object generateRandom {
@JvmStatic
fun main(args: Array<String>) {
// Create instance of SecureRandom class
val rand = SecureRandom()
// Generate random integers in range 0 to 999
val rand_int = rand.nextInt(1000)
// Use rand_int for security & authentication
}
}
Java
import java.security.SecureRandom;
public class generateRandom {
public static void main(String args[])
{
// Create instance of SecureRandom class
SecureRandom rand = new SecureRandom();
// Generate random integers in range 0 to 999
int rand_int = rand.nextInt(1000);
// Use rand_int for security & authentication
}
}
SecureRandom מקבל את ערך ברירת המחדל של ה-seed מ-/dev/urandom, ומשתמשים בו באופן אוטומטי כשיוצרים את האובייקט או מקבלים אותו, כך שאין צורך להגדיר את ה-seed של ה-PRNG באופן מפורש. באופן כללי, לא מומלץ להשתמש ב-SecureRandom באופן דטרמיניסטי (במיוחד אם זה מוביל לכתיבת ערך seed בתוך הקוד, שכל מי שמבצע דקומפילציה של האפליקציה יכול לראות). מפתחים שרוצים ליצור פלט פסאודו-אקראי שניתן לשחזור צריכים להשתמש בפרימיטיבים מתאימים יותר כמו HMAC, HKDF ו-SHAKE.
java.util.Random
לא מומלץ מטעמי אבטחה או אימות, אבל אפשר להשתמש בו לכל מטרה אחרת.
Kotlin
import java.util.Random
object generateRandom {
@JvmStatic
fun main(args: Array<String>) {
// Create instance of SecureRandom class
val rand = Random()
// Generate random integers in range 0 to 999
val rand_int = rand.nextInt(1000)
}
}
Java
import java.util.Random;
public class generateRandom {
public static void main(String args[])
{
// Create instance of Random class
Random rand = new Random();
// Generate random integers in range 0 to 999
int rand_int = rand.nextInt(1000);
}
}
משאבים
- java.security.SecureRandom
- java.util.Random
- Math.random
- Predictable Seed CWE
- Cryptographically Weak PRNG CWE
- Java Secure Random
- ההבדל בין Java Random לבין SecureRandom
- איך משתמשים ב-SecureRandom
- הנחיות אבטחה לגבי PRNG ב-Python
- OWASP Cryptographic Storage Cheat Sheet
- CVE-2013-6386: פגיעות ב-PRNG ב-Drupal
- CVE-2006-3419: פגיעות ב-PRNG ב-Tor
- CVE-2008-4102: ערך בסיס צפוי ב-Joomla
- Linux Kernel random patch