Dự đoán đầu ra của rand PHP ()


21

Tôi đã đọc trong nhiều nguồn rằng đầu ra của rand () của PHP có thể dự đoán được dưới dạng PRNG và tôi hầu như chấp nhận điều đó là thực tế đơn giản vì tôi đã thấy nó ở rất nhiều nơi.

Tôi quan tâm đến một bằng chứng về khái niệm: làm thế nào tôi có thể dự đoán đầu ra của rand ()? Từ khi đọc bài viết này, tôi hiểu rằng số ngẫu nhiên là một số được trả về từ một danh sách bắt đầu từ một con trỏ (hạt giống) - nhưng tôi không thể tưởng tượng được điều này có thể dự đoán được như thế nào.

Ai đó có thể tìm ra hợp lý # ngẫu nhiên nào được tạo thông qua rand () tại một thời điểm nhất định trong vòng vài nghìn lần đoán không? hoặc thậm chí 10.000 lần đoán? Làm sao?

Điều này sắp xảy ra bởi vì tôi thấy một thư viện xác thực sử dụng rand () để tạo mã thông báo cho người dùng bị mất mật khẩu và tôi cho rằng đây là một lỗ hổng bảo mật tiềm năng. Kể từ đó, tôi đã thay thế phương pháp bằng cách băm hỗn hợp openssl_random_pseudo_bytes(), mật khẩu băm gốc và microtime. Sau khi làm điều này, tôi nhận ra rằng nếu tôi ở bên ngoài nhìn vào, tôi không biết làm thế nào để đoán mã thông báo ngay cả khi biết đó là md5 của rand ().


"Nhưng tôi không thể tưởng tượng làm thế nào điều này có thể dự đoán được"? Trước tiên, bạn cần phải đọc " en.wikipedia.org/wiki/Linear_congruential_generator để bạn có thể bắt đầu tưởng tượng nó có thể dự đoán được như thế nào. nguồn chức năng rand để xem nó hoạt động như thế nào
S.Lott

"Tôi cho rằng đây là một lỗ hổng bảo mật tiềm năng"? Chỉ khi Evil Hacker có thể nhận được mật khẩu ngẫu nhiên của người dùng, hãy sử dụng bảng cầu vồng để hoàn tác hàm băm MD5 để khôi phục giá trị ban đầu (tiền băm) và sau đó đảm bảo rằng họ đã thực hiện yêu cầu mật khẩu tiếp theo. Về mặt lý thuyết có thể, tôi cho rằng. Nhưng chỉ khi họ có một bảng cầu vồng làm việc cho một số ngẫu nhiên.
S.Lott

@ S.Lott - đó không phải là vấn đề về mật khẩu. Hệ thống cho phép bạn đặt lại mật khẩu và gửi email cho bạn mã thông báo được sử dụng trong URL. Mã thông báo được tạo thông qua MD5 (rand ()). Nếu bạn có thể dự đoán đầu ra của rand (), bạn có thể thay đổi mật khẩu của bất kỳ ai, mà không cần băm cho bản gốc hoặc biết bản gốc.
Erik

@Erik. Đúng. Thay thế "mật khẩu ngẫu nhiên" bằng "mã thông báo ngẫu nhiên" nếu điều đó có ích. Mã thông báo chỉ có thể bị lạm dụng nếu ai đó có thể giải phóng băm MD5 để khôi phục số ngẫu nhiên VÀ đảm bảo rằng họ sẽ nhận được số ngẫu nhiên tiếp theo. Dự đoán rand tiếp theo chỉ là một phần nhỏ. Hoàn tác MD5 là phần khó.
S.Lott

1
Lưu ý rằng MD5 (rand ()) chỉ có cùng mức bảo mật như rand (). Thật thiết thực khi xây dựng bảng tra cứu MD5 (rand ()) -> rand () cho tập hợp số lượng rất hạn chế có liên quan. Với miền giới hạn của rand (), bạn có thể thử lực lượng đơn giản trừ khi có một cơ chế ngăn chặn các nỗ lực lặp lại.
MZB

Câu trả lời:


28

Khả năng đoán giá trị tiếp theo từ randgắn liền với khả năng xác định những gì srandđược gọi với. Đặc biệt, gieo hạt srandvới một số lượng được xác định trước dẫn đến đầu ra dự đoán ! Từ dấu nhắc tương tác 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 > 

Đây không chỉ là một số sán. Hầu hết các phiên bản PHP * trên hầu hết các nền tảng ** sẽ tạo ra chuỗi 97, 97, 39, 77, 93 khi srandcó 1024.

Để rõ ràng, đây không phải là một vấn đề với PHP, đây là một vấn đề với việc thực hiện randchính nó. Vấn đề tương tự xuất hiện trong các ngôn ngữ khác sử dụng cùng cách thực hiện (hoặc tương tự), bao gồm Perl.

Thủ thuật là bất kỳ phiên bản lành mạnh nào của PHP sẽ được tạo mầm trước srandvới giá trị "không xác định". Ồ, nhưng nó không thực sự là không biết. Từ ext/standard/php_rand.h:

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

Vì vậy, đó là một số phép toán với time(), PID và kết quả của php_combined_lcg, được định nghĩa trong ext/standard/lcg.c. Tôi sẽ không đến đây, vì, đôi mắt tôi trừng trừng và tôi quyết định ngừng săn bắn.

Một chút của Google cho thấy rằng các khu vực khác của PHP không có các thuộc tính tạo ngẫu nhiên tốt nhất và các lệnh gọi để php_combined_lcgnổi bật ở đây, đặc biệt là phân tích này:

Hàm này không chỉ cung cấp gettimeofdaycho chúng ta dấu thời gian chính xác của máy chủ trên đĩa bạc, mà còn thêm vào đầu ra LCG nếu chúng ta yêu cầu "nhiều entropy" hơn (từ PHP uniqid).

Vâng đóuniqid . Có vẻ như giá trị của php_combined_lcglà những gì chúng ta thấy khi chúng ta nhìn vào các chữ số hex kết quả sau khi gọi uniqidvới đối số thứ hai được đặt thành một giá trị thực.

Bây giờ, chúng ta đã ở đâu?

Ồ vâng. srand.

Vì vậy, nếu mã bạn đang cố gắng dự đoán các giá trị ngẫu nhiên từ cuộc gọi khôngsrand , bạn sẽ cần xác định giá trị được cung cấp bởi php_combined_lcg, mà bạn có thể nhận được (gián tiếp?) Thông qua một cuộc gọi đến uniqid. Với giá trị đó trong tay, việc đưa ra phần còn lại của giá trị là khả thi - time(), PID và một số phép toán. Vấn đề bảo mật được liên kết là về việc phá vỡ các phiên, nhưng kỹ thuật tương tự sẽ hoạt động ở đây. Một lần nữa, từ bài viết:

Dưới đây là tóm tắt các bước tấn công được nêu ở trên:
  • chờ máy chủ khởi động lại
  • lấy giá trị uniqid
  • vũ phu buộc hạt giống RNG từ đây
  • thăm dò trạng thái trực tuyến để chờ mục tiêu xuất hiện
  • các cuộc thăm dò tình trạng xen kẽ với các cuộc thăm dò uniqid để theo dõi thời gian máy chủ hiện tại và giá trị RNG
  • ID phiên vũ trang chống lại máy chủ sử dụng khoảng thời gian và giá trị RNG được thiết lập trong bỏ phiếu

Chỉ cần thay thế bước cuối cùng theo yêu cầu.

(Vấn đề bảo mật này đã được báo cáo trong phiên bản PHP trước đó (5.3.2) so với hiện tại (5.3.6), do đó, có thể hành vi của uniqidvà / hoặc php_combined_lcgđã thay đổi, vì vậy kỹ thuật cụ thể này có thể không còn khả thi nữa. YMMV.)

Mặt khác, nếu mã bạn đang cố gắng gọisrand sản phẩm theo cách thủ công , thì trừ khi họ sử dụng thứ gì đó tốt hơn nhiều lần so với kết quả php_combined_lcg, có lẽ bạn sẽ dễ dàng đoán được giá trị và gieo hạt cục bộ của mình hơn máy phát điện với số lượng đúng. Hầu hết mọi người sẽ gọi thủ công srandcũng sẽ không nhận ra ý tưởng này kinh khủng đến mức nào, và do đó không có khả năng sử dụng các giá trị tốt hơn.

Điều đáng chú ý mt_randlà cũng bị ảnh hưởng bởi cùng một vấn đề. Việc gieo hạt mt_srandvới một giá trị đã biết cũng sẽ tạo ra kết quả có thể dự đoán được. Dựa vào entropy của bạn openssl_random_pseudo_bytescó lẽ là một đặt cược an toàn hơn.

tl; dr: Để có kết quả tốt nhất, đừng chọn trình tạo số ngẫu nhiên PHP và vì lợi ích của bạn, đừng để lộ uniqidcho người dùng. Làm một hoặc cả hai điều này có thể khiến các số ngẫu nhiên của bạn dễ đoán hơn.


Cập nhật cho PHP 7:

PHP 7.0 giới thiệu random_bytesrandom_intlà các chức năng cốt lõi. Họ sử dụng triển khai CSPRNG của hệ thống cơ bản, giúp họ không gặp phải các vấn đề mà trình tạo số ngẫu nhiên có hạt giống gặp phải. Chúng có hiệu quả tương tự openssl_random_pseudo_bytes, chỉ khi không cần cài đặt tiện ích mở rộng. Một polyfill có sẵn cho PHP5 .


*: Bản vá bảo mật Suhosin thay đổi hành vi randmt_randsao cho chúng luôn tái tạo hạt giống với mỗi cuộc gọi. Suhosin được cung cấp bởi một bên thứ ba. Một số bản phân phối Linux bao gồm nó trong các gói PHP chính thức của họ theo mặc định, trong khi các bản phân phối khác làm cho nó trở thành một tùy chọn và những bản khác hoàn toàn bỏ qua nó.

**: Tùy thuộc vào nền tảng và các cuộc gọi thư viện cơ bản đang được sử dụng, các chuỗi khác nhau sẽ được tạo ra so với tài liệu ở đây, nhưng kết quả vẫn phải được lặp lại trừ khi sử dụng bản vá Suhosin.


Cảm ơn Charles - giữa câu trả lời của bạn và đọc liên kết về trình tạo đồng dư tuyến tính từ Tangurena tôi cảm thấy tôi hiểu rõ hơn về nó. Tôi đã "biết" rằng sử dụng rand () trong thời trang này là một ý tưởng tồi, nhưng biết tôi biết tại sao .
Erik

Wow, đạo cụ cho một câu trả lời chính tả cũng chính tả, cảm ơn!
David Hobs

10

Để minh họa trực quan mức độ không ngẫu nhiên của rand()hàm, đây là hình ảnh trong đó tất cả các pixel được tạo từ các giá trị đỏ, lục và lam "ngẫu nhiên":

Giá trị RGB ngẫu nhiên

Thông thường không nên có bất kỳ mẫu nào trong ảnh.

Tôi đã thử gọi srand()với các giá trị khác nhau, nó không thay đổi mức độ dự đoán của chức năng này.

Lưu ý rằng cả hai đều không bảo mật bằng mật mã và tạo ra kết quả có thể dự đoán được.


7

đầu ra của rand () của PHP có thể dự đoán được dưới dạng PRNG

Nó là một máy phát đồng quy tuyến tính . Điều đó có nghĩa là bạn có một chức năng có hiệu quả : NEW_NUMBER = (A * OLD_NUMBER + B) MOD C. Nếu bạn lập biểu đồ NEW_NUMBER so với OLD_NUMBER, bạn sẽ bắt đầu thấy các đường chéo. Một số lưu ý trong tài liệu RAND của PHP đưa ra ví dụ về cách thực hiện.

Điều này sắp xảy ra bởi vì tôi thấy một thư viện xác thực sử dụng rand () để tạo mã thông báo cho người dùng bị mất mật khẩu và tôi cho rằng đây là một lỗ hổng bảo mật tiềm năng.

Trên máy tính windows, giá trị tối đa của RAND là 2 ^ 15. Điều này cung cấp cho kẻ tấn công chỉ có 32.768 khả năng để kiểm tra.

Ai đó có thể tìm ra hợp lý # ngẫu nhiên nào được tạo thông qua rand () tại một thời điểm nhất định trong vòng vài nghìn lần đoán không? hoặc thậm chí 10.000 lần đoán? Làm sao?

Mặc dù bài viết này không chính xác là bài viết mà bạn đang tìm kiếm, nhưng nó cho thấy một số nhà nghiên cứu đã thực hiện một trình tạo số ngẫu nhiên hiện có và sử dụng nó để kiếm tiền trên Texas Holdem. Có 52! các sàn được xáo trộn có thể, nhưng việc triển khai đã sử dụng trình tạo số ngẫu nhiên 32 bit (là số tối đa trong số mt_getrandmax trên máy windows) và gieo nó với thời gian tính bằng mili giây kể từ nửa đêm. Điều này đã giảm số lượng các sàn được xáo trộn có thể từ khoảng 2 ^ 226 xuống còn khoảng 2 ^ 27 để có thể tìm kiếm trong thời gian thực và biết sàn nào đã được xử lý.

Sau khi làm điều này, tôi nhận ra rằng nếu tôi ở bên ngoài nhìn vào, tôi không biết làm thế nào để đoán mã thông báo ngay cả khi biết đó là md5 của rand ().

Tôi khuyên bạn nên sử dụng một cái gì đó trong gia đình SHA-2 vì các liên đoàn cho rằng md5 bị hỏng. Một số người sử dụng google để giải mã băm md5 vì chúng rất phổ biến. Chỉ cần băm một cái gì đó sau đó ném băm vào một tìm kiếm google - về cơ bản google đã trở thành một bảng cầu vồng khổng lồ .


1

Thật sự chính xác hơn khi nói rằng với một số được tạo ngẫu nhiên, số tiếp theo là tương đối có thể dự đoán được. Chỉ có rất nhiều con số. Nhưng điều đó không có nghĩa là bạn có thể đoán nó, hơn nữa bạn có thể viết một chương trình đó, khá nhanh chóng.


1
Tôi nghĩ rằng số tiếp theo là hoàn toàn xác định. Không "tương đối" nhưng hoàn toàn. Vấn đề với các trình tạo số giả ngẫu nhiên là một chuỗi sẽ vượt qua các bài kiểm tra thống kê. Hai số liền kề, trong khi hoàn toàn xác định, sẽ có thể có các thuộc tính thống kê chung với các số ngẫu nhiên thực tế.
S.Lott

1
Số tiếp theo là hoàn toàn xác định. Đó là ý nghĩa của "giả" trong trình tạo số giả ngẫu nhiên. Mặt khác, thông tin cần thiết để xác định rằng số tiếp theo là không thể có được trong thực tế.
Rein Henrichs

@ S.Lott - Tôi có ấn tượng rằng một số có thể xuất hiện nhiều lần trong 2 ^ 32 đầu ra có thể và mỗi lần nó xuất hiện có thể được theo sau bởi một số khác nhau. Nhưng được cho một hạt giống X, trả về kết quả của Y, kết quả tiếp theo sẽ luôn giống nhau. Do đó, trong thực tế, có thể có một số ít theo Y. Tôi có thể sai; đó là một thời gian dài kể từ khi tôi thực sự nhìn vào PRNG.
pdr
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.