PRNG חלש

קטגוריית OWASP: MASVS-CRYPTO: קריפטוגרפיה

סקירה כללית

מחולל מספרים פסאודו-אקראיים (PRNG) הוא אלגוריתם שמייצר רצפים של מספרים שניתן לחזות אותם על סמך ערך התחלתי שנקרא seed. לרצף מספרים שנוצר על ידי PRNG יש בערך את אותן תכונות כמו לרצף מספרים אקראיים אמיתי, אבל הוא מהיר יותר ודורש פחות משאבי מחשוב ליצירה.

במילים אחרות, ל-PRNG יש רמת אבטחה גבוהה יותר מאשר ל-RNG חלש (למשל, java.math.Random) מבחינת אחידות של חלוקת האנטרופיה, שמדמה רצפים של מספרים אקראיים באמת. יצירה של מספרים אקראיים באמת דורשת ציוד מיוחד, ולרוב היא לא נכללת בתהליך פיתוח רגיל. המסמך הזה לא עוסק ביצירה של מספרים אקראיים באמת, אלא רק ב-PRNGs, כי זו המתודולוגיה הסטנדרטית שבה נעשה שימוש.

נקודות חולשה ב-PRNG מתרחשות כשהמפתחים משתמשים ב-PRNG רגיל למטרות קריפטוגרפיות, במקום ב-PRNG מאובטח מבחינה קריפטוגרפית (CSPRNG). ל-CSPRNG יש דרישות מחמירות יותר, וכשהערך הראשוני לא ידוע, הן צריכות לתת לתוקף רק יתרון זניח בהבחנה בין רצף פלט לבין רצף אקראי בפועל.

בנוסף, התוקפים יכולים לנחש את רצף המספרים שנוצר אם נעשה שימוש בערכי התחלה צפויים – כמו אלה שמוגדרים בהארדקוד על ידי המפתח – כדי לאתחל PRNG או CSPRNG, כי התוקף יכול לנחש את ערך ההתחלה וכך לחזות את הפלט שנוצר על ידי ה-PRNG.

השפעה

אם משתמשים ב-PRNG לא מאובטח מבחינה קריפטוגרפית בהקשר אבטחתי כמו אימות, יכול להיות שתוקף יוכל לנחש את המספרים שנוצרו באופן אקראי ולקבל גישה לנתונים או לתכונות עם הרשאות מיוחדות.

מיטיגציה

כללי

java.security.SecureRandom

מומלץ לשימושים שקשורים לאבטחה. ב-Android,‏ SecureRandom.getInstanceStrong() שווה ערך לקבלת SHA1PRNG מ-AndroidOpenSSL. היא לא ממופה להטמעה של חסימה /dev/random. כדי להשתמש בו, מתקשרים למספר getInstanceStrong():

Kotlin

val rand = SecureRandom.getInstanceStrong()

Java

SecureRandom rand = SecureRandom.getInstanceStrong();

אחרת, בגרסאות של ליבת Linux שקודמות לגרסה 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);
    }
}

משאבים