Khả năng đoán giá trị tiếp theo từ rand
gắ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 srand
vớ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 srand
có 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 rand
chí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 srand
vớ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_lcg
nổi bật ở đây, đặc biệt là phân tích này:
Hàm này không chỉ cung cấp gettimeofday
cho 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_lcg
là 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 uniqid
vớ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 uniqid
và / 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 srand
cũ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_rand
là cũng bị ảnh hưởng bởi cùng một vấn đề. Việc gieo hạt mt_srand
vớ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_bytes
có 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ộ uniqid
cho 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_bytes
và random_int
là 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 rand
và mt_rand
sao 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.