@@ -125,9 +125,94 @@ function create_captcha($data = '', $img_path = '', $img_url = '', $font_path =
125
125
if (empty ($ word ))
126
126
{
127
127
$ word = '' ;
128
- for ($ i = 0 , $ mt_rand_max = strlen ($ pool ) - 1 ; $ i < $ word_length ; $ i ++)
128
+ $ pool_length = strlen ($ pool );
129
+ $ rand_max = $ pool_length - 1 ;
130
+
131
+ // PHP7 or a suitable polyfill
132
+ if (function_exists ('random_int ' ))
133
+ {
134
+ try
135
+ {
136
+ for ($ i = 0 ; $ i < $ word_length ; $ i ++)
137
+ {
138
+ $ word .= $ pool [random_int (0 , $ rand_max )];
139
+ }
140
+ }
141
+ catch (Exception $ e )
142
+ {
143
+ // This means fallback to the next possible
144
+ // alternative to random_int()
145
+ $ word = '' ;
146
+ }
147
+ }
148
+ }
149
+
150
+ if (empty ($ word ))
151
+ {
152
+ // Nobody will have a larger character pool than
153
+ // 256 characters, but let's handle it just in case ...
154
+ //
155
+ // No, I do not care that the fallback to mt_rand() can
156
+ // handle it; if you trigger this, you're very obviously
157
+ // trying to break it. -- Narf
158
+ if ($ pool_length > 256 )
159
+ {
160
+ return FALSE ;
161
+ }
162
+
163
+ // We'll try using the operating system's PRNG first,
164
+ // which we can access through CI_Security::get_random_bytes()
165
+ $ security = get_instance ()->security ;
166
+
167
+ // To avoid numerous get_random_bytes() calls, we'll
168
+ // just try fetching as much bytes as we need at once.
169
+ if (($ bytes = $ security ->get_random_bytes ($ pool_length )) !== FALSE )
170
+ {
171
+ $ byte_index = $ word_index = 0 ;
172
+ while ($ word_index < $ word_length )
173
+ {
174
+ if (($ rand_index = unpack ('C ' , $ bytes [$ byte_index ++])) > $ rand_max )
175
+ {
176
+ // Was this the last byte we have?
177
+ // If so, try to fetch more.
178
+ if ($ byte_index === $ pool_length )
179
+ {
180
+ // No failures should be possible if
181
+ // the first get_random_bytes() call
182
+ // didn't return FALSE, but still ...
183
+ for ($ i = 0 ; $ i < 5 ; $ i ++)
184
+ {
185
+ if (($ bytes = $ security ->get_random_bytes ($ pool_length )) === FALSE )
186
+ {
187
+ continue ;
188
+ }
189
+
190
+ $ byte_index = 0 ;
191
+ break ;
192
+ }
193
+
194
+ if ($ bytes === FALSE )
195
+ {
196
+ // Sadly, this means fallback to mt_rand()
197
+ $ word = '' ;
198
+ break ;
199
+ }
200
+ }
201
+
202
+ continue ;
203
+ }
204
+
205
+ $ word .= $ pool [$ rand_index ];
206
+ $ word_index ++;
207
+ }
208
+ }
209
+ }
210
+
211
+ if (empty ($ word ))
212
+ {
213
+ for ($ i = 0 ; $ i < $ word_length ; $ i ++)
129
214
{
130
- $ word .= $ pool [mt_rand (0 , $ mt_rand_max )];
215
+ $ word .= $ pool [mt_rand (0 , $ rand_max )];
131
216
}
132
217
}
133
218
elseif ( ! is_string ($ word ))
0 commit comments