PRNG ไม่ดี

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

ภาพรวม

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

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

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

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

ผลกระทบ

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

การบรรเทา

ทั่วไป

java.security.SecureRandom

แนะนำ ให้ใช้เพื่อความปลอดภัย ใน Android, SecureRandom.getInstanceStrong() เทียบเท่ากับการรับ SHA1PRNG จาก AndroidOpenSSL ซึ่งไม่ได้แมปกับการใช้งาน /dev/random ที่บล็อก หากต้องการใช้ ให้เรียก getInstanceStrong() ดังนี้

Kotlin

val rand = SecureRandom.getInstanceStrong()

Java

SecureRandom rand = SecureRandom.getInstanceStrong();

มิฉะนั้น ใน Linux Kernel เวอร์ชันก่อน 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 ซึ่งทุกคนที่คอมไพล์แอปกลับสามารถดูได้) นักพัฒนาแอปที่ต้องการสร้างเอาต์พุตแบบสุ่มเทียมที่ทำซ้ำได้ควรใช้ 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);
    }
}

แหล่งข้อมูล