การทำนายผลลัพธ์ของ PHP ของแรนด์ ()


21

ฉันได้อ่านในหลาย ๆ แหล่งว่าผลลัพธ์ของ rand () ของ PHP นั้นสามารถคาดเดาได้ว่าเป็น PRNG และฉันยอมรับในความเป็นจริงเพียงเพราะฉันเห็นมันในหลาย ๆ ที่

ฉันสนใจในแนวคิดการพิสูจน์: ฉันจะไปทำนายผลของแรนด์ () ได้อย่างไร? จากการอ่านบทความนี้ฉันเข้าใจว่าตัวเลขสุ่มคือตัวเลขที่ส่งคืนจากรายการเริ่มต้นที่ตัวชี้ (เมล็ด) - แต่ฉันไม่สามารถจินตนาการได้ว่าสิ่งนี้จะคาดเดาได้อย่างไร

ใครบางคนสามารถคิดว่า # สุ่มสร้างขึ้นผ่าน rand () ในช่วงเวลาที่กำหนดภายในไม่กี่พันเดา? หรือแม้แต่ 10,000 คำเดา? อย่างไร?

สิ่งนี้กำลังจะเกิดขึ้นเพราะฉันเห็นห้องสมุดรับรองความถูกต้องซึ่งใช้ rand () เพื่อสร้างโทเค็นสำหรับผู้ใช้ที่มีรหัสผ่านหายไปและฉันคิดว่านี่เป็นช่องโหว่ความปลอดภัย ฉันได้แทนที่เมธอดด้วยการแฮชผสมopenssl_random_pseudo_bytes()กับรหัสผ่านแบบแฮชแบบดั้งเดิมและ microtime หลังจากทำสิ่งนี้ฉันก็รู้ว่าถ้าฉันอยู่ข้างนอกฉันก็ไม่รู้ว่าจะเดาโทเค็นได้อย่างไรแม้จะรู้ว่ามันเป็น md5 of rand ()


"แต่ฉันนึกไม่ออกว่ามันจะทายได้อย่างไร" คุณต้องอ่านข้อมูลเกี่ยวกับ " en.wikipedia.org/wiki/Linear_congruential_generatorก่อนเพื่อที่คุณจะสามารถเริ่มจินตนาการได้ว่ามันคาดเดาได้จากนั้นคุณสามารถแก้ไขคำถามของคุณเพื่อกำจัดความประหลาดใจ แหล่งฟังก์ชัน rand เพื่อดูว่ามันทำงานอย่างไร
S.Lott

"ฉันคิดว่านี่เป็นช่องโหว่ความปลอดภัย" เฉพาะในกรณีที่ Evil Hacker สามารถรับรหัสผ่านแบบสุ่มของผู้ใช้ให้ใช้เรนโบว์เทเบิลเพื่อยกเลิกการแฮช MD5 เพื่อกู้คืนค่าดั้งเดิม (แฮชล่วงหน้า) จากนั้นรับประกันได้ว่าพวกเขาได้ทำการร้องขอรหัสผ่านครั้งต่อไป เป็นไปได้ในทางทฤษฎีฉันคิดว่า แต่ถ้าพวกเขามีตารางรุ้งทำงานสำหรับหมายเลขสุ่ม
S.Lott

@ S.Lott - ไม่ใช่เรื่องของรหัสผ่าน ระบบช่วยให้คุณสามารถรีเซ็ตรหัสผ่านและส่งโทเค็นอีเมลซึ่งคุณใช้ใน URL โทเค็นถูกสร้างขึ้นผ่าน MD5 (rand ()) หากคุณสามารถทำนายผลลัพธ์ของแรนด์ () คุณสามารถเปลี่ยนรหัสผ่านของใครก็ได้โดยไม่ต้องมีแฮชสำหรับต้นฉบับหรือรู้ต้นฉบับ
Erik

@Erik ขวา. แทนที่ "รหัสผ่านแบบสุ่ม" ด้วย "random token" หากมีปัญหา โทเค็นสามารถถูกใช้งานได้ต่อเมื่อมีใครสามารถคลายแฮช MD5 เพื่อกู้คืนหมายเลขสุ่มและรับรองว่าพวกเขาจะได้รับหมายเลขสุ่มถัดไป การทำนายแรนด์ถัดไปเป็นเพียงส่วนเล็ก ๆ เท่านั้น การยกเลิก MD5 นั้นเป็นส่วนที่ยาก
S.Lott

1
โปรดทราบว่า MD5 (rand ()) มีความปลอดภัยเช่นเดียวกับ rand () เป็นประโยชน์ในการสร้างตารางการค้นหาของ MD5 (rand ()) -> rand () สำหรับชุดตัวเลขจำนวน จำกัด ที่เกี่ยวข้อง ด้วยโดเมนที่ จำกัด ของ rand () คุณสามารถลองใช้กำลังดุร้ายอย่างง่าย ๆ เว้นแต่จะมีกลไกป้องกันการพยายามซ้ำแล้วซ้ำอีก
MZB

คำตอบ:


28

ความสามารถในการเดาค่าถัดไปจากrandนั้นเชื่อมโยงกับความสามารถในการพิจารณาสิ่งที่srandถูกเรียกด้วย โดยเฉพาะการเพาะที่srandมีจำนวนที่กำหนดไว้จะทำให้ได้ผลลัพธ์ที่คาดการณ์ได้ ! จากพร้อมต์ PHP แบบโต้ตอบ:

[charles@charles-workstation ~]$ php -a
Interactive shell

php > srand(1024);
php > echo rand(1, 100);
97
php > echo rand(1, 100);
97
php > echo rand(1, 100);
39
php > echo rand(1, 100);
77
php > echo rand(1, 100);
93
php > srand(1024);
php > echo rand(1, 100);
97
php > echo rand(1, 100);
97
php > echo rand(1, 100);
39
php > echo rand(1, 100);
77
php > echo rand(1, 100);
93
php > 

นี่ไม่ใช่แค่ความบังเอิญ PHP เวอร์ชันส่วนใหญ่*บนแพลตฟอร์มส่วนใหญ่**จะสร้างลำดับ 97, 97, 39, 77, 93 เมื่อใช้srandกับ 1024

เพื่อความชัดเจนนี่ไม่ใช่ปัญหาของ PHP นี่เป็นปัญหาของการใช้งานrandตัวมันเอง ปัญหาเดียวกันปรากฏในภาษาอื่น ๆ ที่ใช้การใช้งานเดียวกัน (หรือคล้ายกัน) รวมถึง Perl

เคล็ดลับคือ PHP รุ่นใด ๆ ที่มีสติจะมีค่าเริ่มต้นที่srandมีค่า "ไม่ทราบ" โอ้ แต่มันไม่เป็นที่รู้จักจริงๆ จากext/standard/php_rand.h:

#define GENERATE_SEED() (((long) (time(0) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))

ดังนั้นจึงเป็นทางคณิตศาสตร์บางคนที่มีtime(), PID และผลจากการที่กำหนดไว้ในphp_combined_lcg ext/standard/lcg.cฉันจะไม่ไปที่นี่เหมือนกันดวงตาของฉันจ้องมองและฉันก็ตัดสินใจหยุดล่าสัตว์

Googling เล็กน้อยแสดงให้เห็นว่าพื้นที่อื่น ๆ ของ PHP ไม่มีคุณสมบัติการสร้างแบบสุ่มที่ดีที่สุดและการเรียกร้องให้php_combined_lcgโดดเด่นโดยเฉพาะการวิเคราะห์บิตนี้:

ฟังก์ชั่นนี้ไม่เพียง แต่ ( gettimeofday) ส่งเรากลับเวลาประทับของเซิร์ฟเวอร์ที่แม่นยำบนแผ่นเงินมันยังเพิ่มใน LCG เอาท์พุทถ้าเราขอ "เอนโทรปีมากขึ้น" (จากของ PHP uniqid)

ใช่ว่า uniqidดูเหมือนว่าค่าของphp_combined_lcgคือสิ่งที่เราเห็นเมื่อเราดูเลขฐานสิบหกที่เกิดขึ้นหลังจากการเรียกuniqidด้วยอาร์กิวเมนต์ที่สองตั้งค่าที่แท้จริง

ตอนนี้เราอยู่ที่ไหน

โอ้ใช่. srand.

ดังนั้นถ้ารหัสที่คุณกำลังพยายามที่จะคาดการณ์ค่าสุ่มจากไม่ได้โทรหาsrandคุณกำลังจะต้องกำหนดค่าให้โดยphp_combined_lcgที่คุณจะได้รับ (ทางอ้อม?) uniqidผ่านการเรียกไปยัง ด้วยค่าที่มีอยู่ในมือจึงเป็นไปได้ที่จะบังคับค่าที่เหลือ - เดรัจฉาน - time()PID และคณิตศาสตร์ ปัญหาความปลอดภัยที่เชื่อมโยงเป็นเรื่องเกี่ยวกับการแบ่งเซสชัน แต่เทคนิคเดียวกันจะทำงานที่นี่ อีกครั้งจากบทความ:

นี่เป็นบทสรุปของขั้นตอนการโจมตีที่อธิบายไว้ข้างต้น:
  • รอให้เซิร์ฟเวอร์รีบูต
  • ดึงค่า uniqid
  • กำลังดุร้ายกับเมล็ด RNG จากสิ่งนี้
  • สำรวจสถานะออนไลน์เพื่อรอให้เป้าหมายปรากฏ
  • การสอดแทรกสถานะสอดแทรกพร้อมโพล uniqid เพื่อติดตามเวลาเซิร์ฟเวอร์ปัจจุบันและค่า RNG
  • กำลังประมวลผล ID เซสชันที่ดุร้ายกับเซิร์ฟเวอร์โดยใช้เวลาและช่วงค่า RNG ที่สร้างขึ้นในการทำโพล

เพียงแทนที่ขั้นตอนสุดท้ายตามต้องการ

(ปัญหาด้านความปลอดภัยนี้มีการรายงานในเวอร์ชัน PHP ก่อนหน้า (5.3.2) กว่าที่เรามีในปัจจุบัน (5.3.6) ดังนั้นจึงเป็นไปได้ว่าพฤติกรรมของuniqidและ / หรือphp_combined_lcgมีการเปลี่ยนแปลงดังนั้นเทคนิคเฉพาะนี้อาจไม่สามารถใช้งานได้อีกต่อไป YMMV.)

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

เป็นที่น่าสังเกตว่าmt_randปัญหาเดียวกันก็เกิดขึ้นเช่นกัน การเพาะmt_srandด้วยค่าที่ทราบจะทำให้ได้ผลลัพธ์ที่คาดการณ์ได้ การอ้างอิงเอนโทรปีของคุณopenssl_random_pseudo_bytesอาจเป็นทางออกที่ปลอดภัยกว่า

tl; dr:เพื่อให้ได้ผลลัพธ์ที่ดีที่สุดอย่าเริ่มต้นตัวสร้างตัวเลขสุ่ม PHP และเพื่อประโยชน์ของคุณอย่าเปิดเผยuniqidต่อผู้ใช้ การทำอย่างใดอย่างหนึ่งหรือทั้งสองอย่างนี้อาจทำให้หมายเลขสุ่มของคุณเดาได้ง่ายขึ้น


อัพเดทสำหรับ PHP 7:

PHP 7.0 แนะนำrandom_bytesและrandom_intเป็นฟังก์ชั่นหลัก พวกเขาใช้การใช้งาน CSPRNG ของระบบพื้นฐานทำให้พวกเขาเป็นอิสระจากปัญหาที่ตัวสร้างหมายเลขสุ่มมีเมล็ด มันคล้ายกันอย่างมีประสิทธิภาพopenssl_random_pseudo_bytesเพียงโดยไม่ต้องมีส่วนขยายที่จะติดตั้ง polyfill สามารถใช้ได้สำหรับการ PHP5


*: แพทช์รักษาความปลอดภัยของซูโฮซินเปลี่ยนพฤติกรรมของสิ่งนั้นrandและmt_randพวกมันจะทำการรีเรสกับการโทรทุกครั้ง Suhosin ให้บริการโดยบุคคลที่สาม การแจกแจงลินุกซ์บางอย่างรวมไว้ในแพ็คเกจ PHP อย่างเป็นทางการโดยค่าเริ่มต้นในขณะที่คนอื่น ๆ ทำให้มันเป็นตัวเลือกและคนอื่นไม่สนใจมัน

**: ขึ้นอยู่กับแพลตฟอร์มและการเรียกใช้ไลบรารีพื้นฐานลำดับที่แตกต่างกันจะถูกสร้างขึ้นจากที่นี่ แต่ผลลัพธ์จะยังคงสามารถทำซ้ำได้เว้นแต่จะมีการใช้ปะแก้ของ Suhosin


ขอบคุณชาร์ลส์ - ระหว่างคำตอบของคุณและอ่านลิงก์ในตัวสร้างความสอดคล้องเชิงเส้นจาก Tangurena ฉันรู้สึกว่าฉันมีความเข้าใจที่ดีขึ้น ฉันแล้ว "รู้" ว่าการใช้ Rand () ในแบบนี้เป็นความคิดที่ไม่ดี แต่รู้ว่าฉันรู้ว่าทำไม
Erik

ว้าวอุปกรณ์ประกอบฉากสำหรับคำตอบที่สะกดออกมาได้ดีขอบคุณ!
David Hobs

10

เพื่อแสดงให้เห็นถึงการrand()ทำงานของฟังก์ชั่นที่ไม่ใช่การสุ่มนี่คือภาพที่พิกเซลทั้งหมดทำจาก "สุ่ม" ค่าสีแดงสีเขียวและสีน้ำเงิน:

ค่า RGB แบบสุ่ม

ปกติจะไม่มีรูปแบบใด ๆ ในภาพ

ฉันได้ลองโทรsrand()ด้วยค่าที่แตกต่างกันแล้วมันก็ไม่เปลี่ยนวิธีที่ฟังก์ชั่นนี้คาดเดาได้

โปรดทราบว่าทั้งคู่ไม่มีความปลอดภัยในการเข้ารหัสและสร้างผลลัพธ์ที่คาดการณ์ได้


7

ผลลัพธ์ของ rand ของ PHP () สามารถคาดการณ์ได้ว่าเป็น PRNG

มันเป็นเครื่องกำเนิดไฟฟ้าเชิงเส้นความสอดคล้องกัน NEW_NUMBER = (A * OLD_NUMBER + B) MOD Cนั่นหมายความว่าคุณมีฟังก์ชั่นที่มีประสิทธิภาพ: หากคุณสร้างแผนภูมิ NEW_NUMBER กับ OLD_NUMBER คุณจะเห็นเส้นทแยงมุม หมายเหตุบางประการเกี่ยวกับเอกสาร RAND ของ PHPแสดงตัวอย่างของวิธีการดังกล่าว

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

บนเครื่อง windows ค่าสูงสุดของ RAND คือ 2 ^ 15 สิ่งนี้ทำให้ผู้โจมตีมีโอกาสเพียง 32,768 ตัวในการตรวจสอบ

ใครบางคนสามารถคิดว่า # สุ่มสร้างขึ้นผ่าน rand () ในช่วงเวลาที่กำหนดภายในไม่กี่พันเดา? หรือแม้แต่ 10,000 คำเดา? อย่างไร?

ในขณะที่บทความนี้ไม่ตรงกับที่คุณกำลังมองหามันแสดงให้เห็นว่านักวิจัยบางคนนำการดำเนินการของเครื่องกำเนิดตัวเลขแบบสุ่มที่มีอยู่และใช้มันเพื่อสร้างรายได้ในเท็กซัสโฮลด์ มี 52! สำรับสับเปลี่ยนที่เป็นไปได้ แต่การใช้งานนั้นใช้ตัวสร้างตัวเลขสุ่มแบบ 32 บิต (ซึ่งเป็นจำนวนสูงสุดออกมาจาก mt_getrandmax บนเครื่องที่ใช้ Windows) และทำการ seed ด้วยเวลาเป็นมิลลิวินาทีตั้งแต่เที่ยงคืน สิ่งนี้จะลดจำนวนของสำรับสับที่เป็นไปได้จากประมาณ 2 ^ 226 เป็นประมาณ 2 ^ 27 ทำให้สามารถค้นหาได้แบบเรียลไทม์และรู้ว่าสำรับใดได้รับการจัดการ

หลังจากทำสิ่งนี้ฉันก็รู้ว่าถ้าฉันอยู่ข้างนอกฉันก็ไม่รู้ว่าจะเดาโทเค็นได้อย่างไรแม้จะรู้ว่ามันเป็น md5 of rand ()

ฉันขอแนะนำให้ใช้บางอย่างในตระกูลSHA-2เนื่องจาก feds พิจารณา md5 ที่ใช้งานไม่ได้ ผู้ใช้บางคนใช้ google เพื่อถอดรหัส md5 hashes เพราะเป็นเรื่องธรรมดา เพียงแค่แฮชบางสิ่งจากนั้นจึงใส่แฮชลงในการค้นหาของ Google - โดยทั่วไป google ได้กลายเป็นตารางสายรุ้งขนาดใหญ่แล้ว


1

มันมีความถูกต้องมากกว่าที่จะบอกได้ว่าจากตัวเลขที่สร้างขึ้นแบบสุ่มหมายเลขถัดไปสามารถคาดเดาได้ค่อนข้าง มีเพียงตัวเลขมากมายที่สามารถเป็นได้ แต่นั่นไม่ได้หมายความว่าคุณสามารถคาดเดาได้มากกว่าที่คุณสามารถเขียนโปรแกรมที่ทำได้ค่อนข้างเร็ว


1
ฉันคิดว่าหมายเลขต่อไปจะถูกกำหนดทั้งหมด ไม่ใช่ "ค่อนข้าง" แต่อย่างแน่นอน ปัญหาของเครื่องกำเนิดเลขสุ่มหลอกคือว่าลำดับจะผ่านการทดสอบทางสถิติ ตัวเลขสองตัวที่อยู่ติดกันในขณะที่กำหนดทั้งหมดจะมีคุณสมบัติทางสถิติเหมือนกันกับตัวเลขสุ่มจริง
S.Lott

1
หมายเลขถัดไปจะถูกกำหนดทั้งหมด นั่นคือสิ่งที่ "หลอก" ในเครื่องกำเนิดตัวเลขหลอกหลอกหมายถึง ในทางกลับกันข้อมูลที่จำเป็นในการพิจารณาว่าหมายเลขถัดไปเป็นไปไม่ได้ที่จะได้รับในทางปฏิบัติ
Rein Henrichs

@ S.Lott - ฉันรู้สึกว่าตัวเลขอาจปรากฏหลายครั้งในผลลัพธ์ที่เป็นไปได้ 2 ^ 32 และแต่ละครั้งที่ปรากฏอาจตามด้วยตัวเลขที่แตกต่างกัน แต่ให้เมล็ดของ X คืนผลลัพธ์ Y ผลลัพธ์ถัดไปจะเหมือนเดิมเสมอ ดังนั้นในทางปฏิบัติอาจมีตัวเลขจำนวนหนึ่งที่ติดตาม Y ฉันอาจผิด มันนานมากแล้วที่ฉันดู PRNG
สาธารณรัฐประชาธิปไตยประชาชนลาว
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.