PRNG ไม่ดี

หมวดหมู่ OWASP: MASVS-CRYPTO: วิทยาการเข้ารหัส

ภาพรวม

ตัวสร้างตัวเลขสุ่มเทียม (PRNG) คืออัลกอริทึมที่สร้างลำดับตัวเลขที่คาดการณ์ได้โดยอิงตามค่าเริ่มต้นที่เรียกว่าค่าเริ่มต้น ลำดับตัวเลขที่สร้างโดย PRNG มีคุณสมบัติโดยประมาณเหมือนกับลำดับตัวเลขสุ่มจริง แต่สร้างได้เร็วกว่าและใช้การคำนวณน้อยกว่า

กล่าวอีกนัยหนึ่งคือ PRNG มีการรับประกันสูงกว่า RNG ที่อ่อนแอ (เช่น java.math.Random) ในแง่ของความสม่ำเสมอของการกระจายเอนโทรปี ซึ่งจำลอง ลำดับตัวเลขสุ่มอย่างแท้จริง การสร้างตัวเลขสุ่มอย่างแท้จริงต้องใช้อุปกรณ์เฉพาะทางและมักอยู่นอกขอบเขตของการพัฒนาปกติ เอกสารนี้ไม่ครอบคลุมการสร้างตัวเลขสุ่มอย่างแท้จริง และมุ่งเน้นเฉพาะ PRNG เนื่องจากเป็นวิธีการมาตรฐานที่ใช้กัน

ช่องโหว่ PRNG ที่ไม่รัดกุมจะเกิดขึ้นเมื่อนักพัฒนาแอปใช้ PRNG ปกติเพื่อ วัตถุประสงค์ในการเข้ารหัสลับ แทนที่จะใช้ PRNG ที่มีความปลอดภัยแบบเข้ารหัสลับ (CSPRNG) CSPRNG มีข้อกำหนดที่เข้มงวดกว่า และเมื่อไม่ทราบค่าเริ่มต้น จะต้องให้ ผู้โจมตีได้เปรียบเพียงเล็กน้อยในการแยกความแตกต่างของลำดับเอาต์พุต จากลำดับแบบสุ่มจริง

ผู้โจมตียังอาจคาดเดาลำดับตัวเลขที่สร้างขึ้นได้เมื่อใช้ Seed ที่คาดเดาได้ เช่น Seed ที่นักพัฒนาซอฟต์แวร์ฮาร์ดโค้ดไว้ เพื่อเริ่มต้น PRNG หรือ CSPRNG เนื่องจากผู้โจมตีสามารถคาดเดา Seed และคาดเดาเอาต์พุตที่สร้างโดย PRNG ได้

ผลกระทบ

หากใช้ PRNG ที่ไม่ปลอดภัยทางคริปโตกราฟีในบริบทด้านความปลอดภัย เช่น การตรวจสอบสิทธิ์ ผู้โจมตีอาจคาดเดาตัวเลขที่สร้างขึ้นแบบสุ่ม และเข้าถึงข้อมูลหรือฟีเจอร์ที่มีสิทธิ์ได้

การลดปัญหา

ทั่วไป

java.security.SecureRandom

แนะนำสำหรับการใช้งานด้านความปลอดภัย หากเวอร์ชันเคอร์เนล Linux เป็น 5.17 ขึ้นไปหรือ ยอมรับการบล็อกเธรดได้ ให้รอจนกว่าจะมีการสะสมเอนโทรปีมากพอ ก่อนที่จะสร้างเลขสุ่ม (เช่น ใช้ /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 จะรับค่าเริ่มต้นจาก /dev/urandom และจะใช้โดยอัตโนมัติ เมื่อสร้างหรือรับออบเจ็กต์ จึงไม่จำเป็นต้อง ระบุค่าเริ่มต้นสำหรับ PRNG อย่างชัดเจน โดยทั่วไป เราไม่แนะนำให้ใช้ SecureRandom แบบดีเทอร์มินิสติก (โดยเฉพาะหากการดำเนินการนี้ทำให้ต้องฮาร์ดโค้ดค่าเริ่มต้น ซึ่ง ทุกคนที่คอมไพล์แอปจะเห็น) นักพัฒนาแอปที่ต้องการสร้างเอาต์พุตแบบสุ่มเทียมที่ทำซ้ำได้ควรใช้ Primitive ที่เหมาะสมกว่า เช่น 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);
    }
}

แหล่งข้อมูล