user_password()
uses the following code.
// Loop the number of times specified by $length.
for ($i = 0; $i < $length; $i++) {
do {
// Find a secure random number within the range needed.
$index = ord(random_bytes(1));
} while ($index > $len);
// Each iteration, pick a random character from the
// allowable string and append it to the password:
$pass .= $allowable_characters[$index];
It's quite an inefficient way to get 10 numbers between 0 and 56. In a simple test I did, I had to call ord(random_bytes(1))
51 times to get 10 numbers between 0 and 56. (The result indeed changes each time the test is done.)
105 216 236 199 33 78 25 98 23 46 122 177 131 80 252 22 202 16 157 201 109 161 188 195 237 174 81 133 59 62 140 65 125 11 47 147 188 66 131 228 134 157 186 91 56 239 194 168 186 183 51 194 202 77 32 161 31 93 87 52 48 121 164 168 206 129 229 215 221 252 84 111 49 165 28 36 64 81 53 220 208 182 182 92 126 91 68 2 114 242 46 84 234 91 174 45 126 145 239 208 151 234 104 29 233 27 183 27 236 122 91 34 229 175 217 57 226 166 130 25 150 229 90 116 237 116 175 186 178 221 214 132 198 28 213 209 132 162 232 194 139 115 170 0 133 193 157 220 23 174 60 61 114 195 143 6 97 113 26 105 163 26 80 29 146 76 201 98 189 205 201 79 172 150 187 255 163 111 204 52 176 212 169 23 33 176 231 19 244 195 230 208 232 218 208 142 175 32 202 54 215 173 138 247 62 189 34 185 113 85 84 16 155 115 205 240 10 154 240 4 129 98 182 35 203 77 220 153 156 166 83 168 54 71 100 17 12 43 221 225 53 25 210 13 117 21 47 55 121 223 168 70 5 251 232 138 35 88 243 250 51 151 50 30 219 129 178 255 215 26 213 86 145 255 142 87 214 17 46 219 87 14 40 100 31 31 212 112 181 13 214 176 237 131 101 229 229 241 109 122
This is expected: random_bytes()
generates a string of cryptographic random bytes and the probability to get a byte in a range should not be higher than the probability to get a byte in a different range.
PHP 7 offers random_int()
which generates cryptographic random integers.
Calling random_int()
300 times (as in the previous test), I get the following numbers.
20 24 3 48 45 52 30 3 19 2 24 18 17 43 21 31 2 3 40 0 13 29 15 4 25 43 19 8 4 40 51 20 25 52 20 33 15 21 23 27 33 4 51 44 30 24 37 9 41 39 41 39 47 54 17 53 14 53 24 46 30 14 30 1 33 8 23 16 1 28 45 26 10 25 15 19 29 52 21 21 6 27 56 51 21 5 6 16 55 18 51 40 25 5 9 56 31 40 6 22 23 7 36 46 17 23 12 51 50 45 33 51 9 13 6 43 0 41 11 3 5 30 42 19 47 10 34 1 22 12 32 8 5 24 17 41 30 21 47 55 11 49 56 50 11 38 23 26 50 29 32 2 39 27 24 55 49 15 26 52 12 55 38 11 32 9 43 32 16 47 27 17 8 53 29 2 15 27 3 54 49 38 28 53 15 37 53 20 0 11 3 14 29 49 4 44 0 9 33 18 9 47 33 43 37 41 21 52 8 41 34 26 40 32 53 14 1 26 36 54 54 34 14 23 15 25 8 11 52 21 2 20 45 56 22 10 25 30 35 4 18 42 46 13 14 55 36 42 19 32 8 32 34 21 33 55 43 14 50 12 50 33 38 36 34 47 6 10 5 1 44 2 16 33 40 56 50 30 7 26 55 31 53 56 35 17 10 2 10 6 12 32 47 21 22 5 29 9 44 51
user_password()
should be updated to use random_int()
instead of random_bytes()
.
Comment | File | Size | Author |
---|---|---|---|
#12 | drupal-update-user_password-3107371-12.patch | 1.8 KB | apaderno |
#10 | drupal-update-user_password-3107371-10.patch | 1.82 KB | apaderno |
#7 | drupal-update-user_password-3107371-7.patch | 1.33 KB | apaderno |
#3 | drupal-update-user_password-3107371-2.patch | 719 bytes | apaderno |
Comments
Comment #2
apadernoComment #3
apadernoComment #4
pandaski CreditAttribution: pandaski commentedThe change looks fine.
A quick question to the random_int parameters - min and max
They are per-defined constants in PHP. Will this lead to some issues in custom settings?
min
The lowest value to be returned, which must be PHP_INT_MIN or higher.
max
The highest value to be returned, which must be less than or equal to PHP_INT_MAX.
Comment #5
apadernoThe parameters passed to
random_int()
don't depend from custom settings, but are 0, and the length of the string contained in$allowable_characters
minus 1. They surely respect the constraints reported in the documentation forrandom_int()
, even on 32-bit PHP.Comment #6
longwaveWe have more comments than code now. Do we really need a comment that describes what a simple "for" loop does?
Comment #7
apaderno@longwave I agree there are comments that describe something that is explicit from the code, for example the Declare the password as a blank string. one. (I would argue the variable is not being declared, but initialized.) The only useful comment is the one explaining why some alphanumeric characters have been removed from the allowed characters.
I removed some comments and added the documentation comments for parameters and returned value.
Comment #8
apadernoI would also rename the
$allowable_characters
variable$allowed_characters
.Comment #9
longwaveGreat improvements! We have $len and $length, I wonder if renaming $len to $max (as in, the maximum random number) makes this slightly more readable?
Comment #10
apadernoComment #11
longwaveA nice modernisation, and documentation improvements on top.
Comment #12
apadernoI changed the comment I added for
$max
from The maximum value for the random integer we get from random_int(). to The maximum integer we want from random_int(). (This is my last change, if nobody points out other changes that should be done.)Comment #13
alexpottCommitted and pushed 06f5e2503d to 9.0.x and d0c7aa1beb to 8.9.x. Thanks!
Fixing the above code style errors on commit.