{"id":10,"date":"2015-05-20T09:51:24","date_gmt":"2015-05-20T09:51:24","guid":{"rendered":"http:\/\/piratesecurityblog.com\/?p=10"},"modified":"2016-09-28T09:51:57","modified_gmt":"2016-09-28T09:51:57","slug":"mostly-good-password-resets","status":"publish","type":"post","link":"https:\/\/piratesecurityblog.com\/?p=10","title":{"rendered":"(Mostly) good password resets"},"content":{"rendered":"<p>This is part 3 to my 2-part series on password reset attacks (<a href=\"http:\/\/www.skullsecurity.org\/blog\/2011\/hacking-crappy-password-resets-part-1\">Part 1<\/a> \/ <a href=\"http:\/\/www.skullsecurity.org\/blog\/2011\/hacking-crappy-password-resets-part-2\">Part 2<\/a>). Overall, I got awesome feedback on the first two parts, but I got the same question over and over: what&#8217;s the RIGHT way to do this?<\/p>\n<p>So, here&#8217;s the thing. I like to break stuff, but I generally leave the fixing to somebody else. It&#8217;s just safer that way, since I&#8217;m not really a developer or anything like that. Instead, I&#8217;m going to continue the trend of looking at others&#8217; implementations by looking at three major opensource projects &#8211; WordPress, SMF, and MediaWiki. Then, since all of these rely on PHP&#8217;s random number implementation to some extent, I&#8217;ll take a brief look at PHP.<br \/>\n<span id=\"more-1088\"><\/span><\/p>\n<h2>SMF<\/h2>\n<p><a href=\"http:\/\/www.simplemachines.org\/\">SMF<\/a> 1.1.13 implements the password-reset function in Sources\/Subs-Auth.php:<\/p>\n<pre>\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ Generate a random password.<\/span>\r\n\u00a0\u00a0<span style=\"color: #ff80ff;\">require_once<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">sourcedir<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.<\/b><\/span>\u00a0'<span style=\"color: #ffa0a0;\">\/Subs-Members.php<\/span>'<span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">newPassword<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0generateValidationCode<span style=\"color: #ffa500;\">()<\/span>;\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">newPassword_sha1<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">sha1<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #40ffff;\">strtolower<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">user<\/span><span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">newPassword<\/span><span style=\"color: #ffa500;\">)<\/span>;<\/pre>\n<p>Looking at Sources\/Subs-Members.php, we find:<\/p>\n<pre><span style=\"color: #80a0ff;\">\/\/ Generate a random validation code.<\/span>\r\n<span style=\"color: #ff80ff;\">function<\/span>\u00a0generateValidationCode<span style=\"color: #ffa500;\">()<\/span>\r\n<span style=\"color: #ffa500;\">{<\/span>\r\n\u00a0\u00a0<span style=\"color: #60ff60;\"><b>global<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">modSettings<\/span>;\r\n\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">request<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0db_query<span style=\"color: #ffa500;\">(<\/span>'\r\n<span style=\"color: #ffa0a0;\">\u00a0\u00a0\u00a0\u00a0SELECT RAND()<\/span>', <span style=\"color: #ffa0a0;\">__FILE__<\/span>, <span style=\"color: #ffa0a0;\">__LINE__<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\r\n\u00a0\u00a0<span style=\"color: #60ff60;\"><b>list<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">dbRand<\/span><span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">mysql_fetch_row<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">request<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0<span style=\"color: #40ffff;\">mysql_free_result<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">request<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>return<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">substr<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #40ffff;\">preg_replace<\/span><span style=\"color: #ffa500;\">(<\/span>'<span style=\"color: #ffa0a0;\">\/\\W\/<\/span>', '', <span style=\"color: #40ffff;\">sha1<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #40ffff;\">microtime<\/span><span style=\"color: #ffa500;\">()<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">mt_rand<\/span><span style=\"color: #ffa500;\">()<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">dbRand<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.<\/b><\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">modSettings<\/span><span style=\"color: #ffa500;\">[<\/span>'<span style=\"color: #ffa0a0;\">rand_seed<\/span>'<span style=\"color: #ffa500;\">]))<\/span>, <span style=\"color: #ffa0a0;\">0<\/span>, <span style=\"color: #ffa0a0;\">10<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n<span style=\"color: #ffa500;\">}<\/span>\r\n<\/pre>\n<p>Which is pretty straight forward, but also, in my opinion, very strong. It takes entropy from a bunch of different places:<\/p>\n<ul>\n<li>The current time (microtime())<\/li>\n<li>PHP&#8217;s random number generator (mt_rand())<\/li>\n<li>MySQL&#8217;s random number generator ($dbRand)<\/li>\n<li>A user-configurable random seed<\/li>\n<\/ul>\n<p>Essentially, it puts these difficult-to-guess values through a cryptographically secure function, sha1(), and takes the first 10 characters of the hash.<\/p>\n<p>The hash consists of lowercase letters and numbers, which means there are 36 possible choices for 10 characters, for a total of 36<sup>10<\/sup> or 3,656,158,440,062,976 possible outputs. That isn&#8217;t as strong as it *could* be, since there&#8217;s no reason to limit its length to 10 characters (or its character set to 36 characters). That being said, three quadrillion different passwords would be nearly impossible to guess. (By my math, exhaustively cracking all possible passwords, assuming md5 cracks at 5 million guesses\/second, would take about 23 CPU-years). Not that cracking is terribly useful &#8211; remote bruteforce guessing is much more useful and is clearly impossible.<\/p>\n<p>SMF is my favourite implementation of the three, but let&#8217;s take a look at WordPress!<\/p>\n<h2>WordPress<\/h2>\n<p><a href=\"http:\/\/wordpress.org\/\">WordPress<\/a> 3.1 implements the password-reset function in wp-login.php:<\/p>\n<pre>\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">key<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">wpdb<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>get_var<span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">wpdb<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>prepare<span style=\"color: #ffa500;\">(<\/span>\"<span style=\"color: #ffa0a0;\">SELECT user_activation_key FROM<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">wpdb<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>users<span style=\"color: #ffa0a0;\">\u00a0WHERE user_login = %s<\/span>\", <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">user_login<\/span><span style=\"color: #ffa500;\">))<\/span>;\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>empty<\/b><\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">key<\/span><span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">{<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ Generate something random for a key...<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">key<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0wp_generate_password<span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffa0a0;\">20<\/span>, <span style=\"color: #ffa0a0;\">false<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0do_action<span style=\"color: #ffa500;\">(<\/span>'<span style=\"color: #ffa0a0;\">retrieve_password_key<\/span>', <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">user_login<\/span>, <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">key<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ Now insert the new md5 key into the db<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">wpdb<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>update<span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">wpdb<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>users, <span style=\"color: #60ff60;\"><b>array<\/b><\/span><span style=\"color: #ffa500;\">(<\/span>'<span style=\"color: #ffa0a0;\">user_activation_key<\/span>'\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span><span style=\"color: #ffff60;\"><b>&gt;<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">key<\/span><span style=\"color: #ffa500;\">)<\/span>, \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: #60ff60;\"><b>array<\/b><\/span><span style=\"color: #ffa500;\">(<\/span>'<span style=\"color: #ffa0a0;\">user_login<\/span>'\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span><span style=\"color: #ffff60;\"><b>&gt;<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">user_login<\/span><span style=\"color: #ffa500;\">))<\/span>;\r\n\u00a0\u00a0<span style=\"color: #ffa500;\">}<\/span>\r\n<\/pre>\n<p>wp_generate_password() is found in wp-includes\/pluggable.php:<\/p>\n<pre><span style=\"color: #80a0ff;\">\/**<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0* Generates a random password drawn from the defined set of characters.<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0*<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0* @since 2.5<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0*<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0* @param int $length The length of password to generate<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0* @param bool $special_chars Whether to include standard special characters.\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Default true.<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0* @param bool $extra_special_chars Whether to include other special characters.<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0*\u00a0\u00a0 Used when generating secret keys and salts. Default false.<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0* @return string The random password<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0**\/<\/span>\r\n<span style=\"color: #ff80ff;\">function<\/span>\u00a0wp_generate_password<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">length<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">12<\/span>, <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">special_chars<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">true<\/span>, <span style=\"color: #ffff60;\"><b>$<\/b><\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: #40ffff;\">extra_special_chars<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">false<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">{<\/span>\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">chars<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0'<span style=\"color: #ffa0a0;\">abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789<\/span>';\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">special_chars<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">chars<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.=<\/b><\/span>\u00a0'<span style=\"color: #ffa0a0;\">!@#$%^&amp;*()<\/span>';\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">extra_special_chars<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">chars<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.=<\/b><\/span>\u00a0'<span style=\"color: #ffa0a0;\">-_ []{}&lt;&gt;~`+=,.;:\/?|<\/span>';\r\n\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">password<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0'';\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>for<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">i<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">0<\/span>; <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">i<\/span>\u00a0<span style=\"color: #ffff60;\"><b>&lt;<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">length<\/span>; <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">i<\/span><span style=\"color: #ffff60;\"><b>++<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">{<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">password<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">substr<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">chars<\/span>, wp_rand<span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffa0a0;\">0<\/span>, <span style=\"color: #40ffff;\">strlen<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">chars<\/span><span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffff60;\"><b>-<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">1<\/span><span style=\"color: #ffa500;\">)<\/span>, <span style=\"color: #ffa0a0;\">1<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0<span style=\"color: #ffa500;\">}<\/span>\r\n\r\n\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ random_password filter was previously in random_password function which was\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0deprecated<\/span>\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>return<\/b><\/span>\u00a0apply_filters<span style=\"color: #ffa500;\">(<\/span>'<span style=\"color: #ffa0a0;\">random_password<\/span>', <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">password<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n<span style=\"color: #ffa500;\">}<\/span>\r\n<\/pre>\n<p>This generates a string of random characters (and possibly symbols) up to a defined length, choosing the characters using wp_rand(). So, for the final step, how is wp_rand() implemented? It&#8217;s also found in wp-includes\/pluggable.php and looks like this:<\/p>\n<pre>\u00a0\u00a0<span style=\"color: #60ff60;\"><b>global<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span>;\r\n\r\n\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ Reset $rnd_value after 14 uses<\/span>\r\n\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ 32(md5) + 40(sha1) + 40(sha1) \/ 8 = 14 random numbers from $rnd_value<\/span>\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #40ffff;\">strlen<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span><span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffff60;\"><b>&lt;<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">8<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">{<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #40ffff;\">defined<\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0'<span style=\"color: #ffa0a0;\">WP_SETUP_CONFIG<\/span>'\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: #60ff60;\"><b>static<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">seed<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0'';\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>else<\/b><\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">seed<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0get_transient<span style=\"color: #ffa500;\">(<\/span>'<span style=\"color: #ffa0a0;\">random_seed<\/span>'<span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">md5<\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #40ffff;\">uniqid<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #40ffff;\">microtime<\/span><span style=\"color: #ffa500;\">()<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">mt_rand<\/span><span style=\"color: #ffa500;\">()<\/span>, <span style=\"color: #ffa0a0;\">true<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">seed<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">sha1<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">sha1<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">seed<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">seed<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">md5<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">seed<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>!<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">defined<\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0'<span style=\"color: #ffa0a0;\">WP_SETUP_CONFIG<\/span>'\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0set_transient<span style=\"color: #ffa500;\">(<\/span>'<span style=\"color: #ffa0a0;\">random_seed<\/span>', <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">seed<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0<span style=\"color: #ffa500;\">}<\/span>\r\n\r\n\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ Take the first 8 digits for our value<\/span>\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">value<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">substr<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span>, <span style=\"color: #ffa0a0;\">0<\/span>, <span style=\"color: #ffa0a0;\">8<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\r\n\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ Strip the first eight, leaving the remainder for the next call to wp_rand().<\/span>\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">substr<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">rnd_value<\/span>, <span style=\"color: #ffa0a0;\">8<\/span><span style=\"color: #ffa500;\">)<\/span>;\r\n\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">value<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">abs<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #40ffff;\">hexdec<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">value<\/span><span style=\"color: #ffa500;\">))<\/span>;\r\n\r\n\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ Reduce the value to be within the min - max range<\/span>\r\n\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ 4294967295 = 0xffffffff = max random number<\/span>\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">max<\/span>\u00a0<span style=\"color: #ffff60;\"><b>!=<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">0<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">value<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">min<\/span>\u00a0<span style=\"color: #ffff60;\"><b>+<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">((<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">max<\/span>\u00a0<span style=\"color: #ffff60;\"><b>-<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">min<\/span>\u00a0<span style=\"color: #ffff60;\"><b>+<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">1<\/span><span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffff60;\"><b>*<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">value<\/span>\u00a0<span style=\"color: #ffff60;\"><b>\/<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffa0a0;\">4294967295<\/span>\u00a0<span style=\"color: #ffff60;\"><b>+<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">1<\/span><span style=\"color: #ffa500;\">)))<\/span>;\r\n\r\n\u00a0\u00a0<span style=\"color: #ffff60;\"><b>return<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">abs<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #40ffff;\">intval<\/span><span style=\"color: #ffa500;\">(<\/span><span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">value<\/span><span style=\"color: #ffa500;\">))<\/span>;\r\n<span style=\"color: #ffa500;\">}<\/span>\r\n<\/pre>\n<p>This is quite complex for generating a number! But the points of interest are:<\/p>\n<ul>\n<li>Hashing functions (sha1 and md5) are used, which are going to be <em>a lot<\/em> slower than a standard generator, but they, at least in theory, have cryptographic strength<\/li>\n<li>The random number is seeded with microtime() and mt_rand(), which is PHP&#8217;s &#8220;advanced&#8221; randomization function)<\/li>\n<li>The random number is restricted to 0 &#8211; 0xFFFFFFFF, which is pretty typical<\/li>\n<\/ul>\n<p>In practice, due to the multiple seeds with difficult-to-predict values and the use of a hashing function to generate strong random numbers, this seems to be a good implementation of a password reset. My biggest concern is the complexity &#8211; using multiple hashing algorithms and hashing in odd ways (like hasing the value alone, then the hash with the seed). It has the feeling of being unsure what to do, so trying to do everything &#8216;just in case&#8217;. While I don&#8217;t expect to find any weaknesses in the implementation, it&#8217;s a little concerning.<\/p>\n<p>Now, let&#8217;s take a look at my least favourite (although still reasonably strong) password-reset implementation: MediaWiki!<\/p>\n<h2>MediaWiki<\/h2>\n<p><a href=\"http:\/\/www.mediawiki.org\/\">MediaWiki<\/a> 1.16.2 was actually the most difficult to find the password reset function in. Eventually, though, I managed to track it down to includes\/specials\/SpecialUserlogin.php:<\/p>\n<pre>\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">np<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">u<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>randomPassword<span style=\"color: #ffa500;\">()<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">u<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>setNewpassword<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">np<\/span>, <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">throttle<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">u<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>saveSettings<span style=\"color: #ffa500;\">()<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">userLanguage<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">u<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>getOption<span style=\"color: #ffa500;\">(<\/span>\u00a0'<span style=\"color: #ffa0a0;\">language<\/span>'\u00a0<span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">m<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0wfMsgExt<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">emailText<\/span>, <span style=\"color: #60ff60;\"><b>array<\/b><\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0'<span style=\"color: #ffa0a0;\">parsemag<\/span>', '<span style=\"color: #ffa0a0;\">language<\/span>'\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span><span style=\"color: #ffff60;\"><b>&gt;<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">userLanguage<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>, \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">ip<\/span>, <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">u<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>getName<span style=\"color: #ffa500;\">()<\/span>, <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">np<\/span>,\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">wgServer<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">wgScript<\/span>, <span style=\"color: #40ffff;\">round<\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">wgNewPasswordExpiry<\/span>\u00a0<span style=\"color: #ffff60;\"><b>\/<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">86400<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">result<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">u<\/span><span style=\"color: #60ff60;\"><b>-&gt;<\/b><\/span>sendMail<span style=\"color: #ffa500;\">(<\/span>\u00a0wfMsgExt<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">emailTitle<\/span>, <span style=\"color: #60ff60;\"><b>array<\/b><\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0'<span style=\"color: #ffa0a0;\">parsemag<\/span>', \r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0'<span style=\"color: #ffa0a0;\">language<\/span>'\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span><span style=\"color: #ffff60;\"><b>&gt;<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">userLanguage<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>, <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">m<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>;\r\n<\/pre>\n<p>$u-&gt;randomPassword() is found in includes\/User.php looks like this:<\/p>\n<pre>\u00a0\u00a0<span style=\"color: #80a0ff;\">\/**<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0\u00a0 * Return a random password. Sourced from mt_rand, so it's not particularly secure.<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0\u00a0 * @<\/span><span style=\"color: #0000ff;\">todo<\/span><span style=\"color: #80a0ff;\">\u00a0hash random numbers to improve security, like generateToken()<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0\u00a0 *<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0\u00a0 * @return \\string New random password<\/span>\r\n<span style=\"color: #80a0ff;\">\u00a0\u00a0 *\/<\/span>\r\n\u00a0\u00a0<span style=\"color: #60ff60;\"><b>static<\/b><\/span>\u00a0<span style=\"color: #ff80ff;\">function<\/span>\u00a0randomPassword<span style=\"color: #ffa500;\">()<\/span>\u00a0<span style=\"color: #ffa500;\">{<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #60ff60;\"><b>global<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">wgMinimalPasswordLength<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">pwchars<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0'<span style=\"color: #ffa0a0;\">ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz<\/span>';\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">l<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">strlen<\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">pwchars<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffff60;\"><b>-<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">1<\/span>;\r\n\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">pwlength<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">max<\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffa0a0;\">7<\/span>, <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">wgMinimalPasswordLength<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">digit<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">mt_rand<\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffa0a0;\">0<\/span>, <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">pwlength<\/span>\u00a0<span style=\"color: #ffff60;\"><b>-<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">1<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">np<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0'';\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>for<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">i<\/span>\u00a0<span style=\"color: #ffff60;\"><b>=<\/b><\/span>\u00a0<span style=\"color: #ffa0a0;\">0<\/span>; <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">i<\/span>\u00a0<span style=\"color: #ffff60;\"><b>&lt;<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">pwlength<\/span>; <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">i<\/span><span style=\"color: #ffff60;\"><b>++<\/b><\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">{<\/span>\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">np<\/span>\u00a0<span style=\"color: #ffff60;\"><b>.=<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">i<\/span>\u00a0<span style=\"color: #ffff60;\"><b>==<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">digit<\/span>\u00a0<span style=\"color: #ffff60;\"><b>?<\/b><\/span>\u00a0<span style=\"color: #40ffff;\">chr<\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #40ffff;\">mt_rand<\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffa0a0;\">48<\/span>, <span style=\"color: #ffa0a0;\">57<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffff60;\"><b>:<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">pwchars<\/span><span style=\"color: #ffa500;\">{<\/span>\u00a0<span style=\"color: #40ffff;\">mt_rand<\/span><span style=\"color: #ffa500;\">(<\/span>\u00a0<span style=\"color: #ffa0a0;\">0<\/span>, <span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">l<\/span>\u00a0<span style=\"color: #ffa500;\">)<\/span>\u00a0<span style=\"color: #ffa500;\">}<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffa500;\">}<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>return<\/b><\/span>\u00a0<span style=\"color: #ffff60;\"><b>$<\/b><\/span><span style=\"color: #40ffff;\">np<\/span>;\r\n\u00a0\u00a0<span style=\"color: #ffa500;\">}<\/span>\r\n<\/pre>\n<p>This is easily the most complex, and also most dangerous, password-reset implementation that I&#8217;ve found.<\/p>\n<p>First, the length is only 7 characters by default. That&#8217;s already an issue.<\/p>\n<p>Second, the set of characters is letters (uppercase + lowercase) and exactly one number. And it looks to me like they put a lot of effort into figuring out just how to put that one number into the password. Initially, I thought this made the password slightly weaker due to the following calculations:<\/p>\n<ul>\n<li>7 characters @ 52 choices = 52<sup>7<\/sup> = 1,028,071,702,528<\/li>\n<li>6 characters @ 52 choices + 1 character @ 10 choices = 52<sup>6<\/sup> * 10 = 197,706,096,640<\/li>\n<\/ul>\n<p>However, as my friend pointed out, because you don&#8217;t know where, exactly, the number will be placed, that actually adds an extra multiplier to the strength:<\/p>\n<ul>\n<li>6 characters @ 52 choices + 1 characters @ 10 choices + unknown number location = 52<sup>6<\/sup> * 10 * 7 = 1,383,942,676,480<\/li>\n<\/ul>\n<p>So, in reality, adding a single number does improve the strength, but only by a bit.<\/p>\n<p>Even with the extra number, though, the best we have at 7 characters is about 1.4 trillion choices. As with the others, that&#8217;s essentially impossible to guess\/bruteforce remotely. That&#8217;s a good thing. However, with a password cracker and 5 million checks\/second, it would take a little over 3.2 CPU-days to exhaustively crack all generated passwords, so that can very easily be achieved.<\/p>\n<p>The other issue here is that the only source of entropy is PHP&#8217;s mt_rand() function. The next section will look at how PHP seeds this function.<\/p>\n<h2>PHP<\/h2>\n<p>All three of these implementations depend, in one way or another, on <a href=\"http:\/\/www.php.net\/\">PHP<\/a>&#8216;s mt_rand() function. The obvious question is, how strong is mt_rand()?<\/p>\n<p>I&#8217;m only going to look at this from a high level for now. When I have some more time, I&#8217;m hoping to dig deeper into this and, with luck, bust it wide open. Stay tuned for that. \ud83d\ude42<\/p>\n<p>For now, though, let&#8217;s look at the function that&#8217;s used by all three password-reset functions: mt_rand(). mt_rand() is an implementation of the <a href=\"https:\/\/secure.wikimedia.org\/wikipedia\/en\/wiki\/Mersenne_Twister\">Mersenne Twister<\/a>algorithm, which is a well tested random number generator with an advertised average period of 2<sup>19937<\/sup>-1. That means that it won&#8217;t repeat until 2<sup>19937<\/sup>-1 values are generated. I don&#8217;t personally have the skills to analyze the strength of the algorithm itself, but what I CAN look at is the seed.<\/p>\n<p>Whether using rand() or mt_rand(), PHP automatically seeds the random number generator. The code is in ext\/standard\/rand.c, and looks like this:<\/p>\n<pre>PHPAPI <span style=\"color: #60ff60;\"><b>long<\/b><\/span>\u00a0php_rand(TSRMLS_D)\r\n{\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #60ff60;\"><b>long<\/b><\/span>\u00a0ret;\r\n\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0(!BG(rand_is_seeded)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0php_srand(GENERATE_SEED() TSRMLS_CC);\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #80a0ff;\">\/\/ ...<\/span>\r\n}\r\n<\/pre>\n<p>Simple enough &#8211; if rand() is called without a seed, then seed it with the GENERATE_SEED() macro, which is found in ext\/standard\/php_rand.h:<\/p>\n<pre><span style=\"color: #ff80ff;\">#ifdef PHP_WIN32<\/span>\r\n<span style=\"color: #ff80ff;\">#define GENERATE_SEED() (((<\/span><span style=\"color: #60ff60;\"><b>long<\/b><\/span><span style=\"color: #ff80ff;\">) (time(<\/span><span style=\"color: #ffa0a0;\">0<\/span><span style=\"color: #ff80ff;\">) * GetCurrentProcessId())) ^ \r\n\u00a0\u00a0\u00a0\u00a0\u00a0((<\/span><span style=\"color: #60ff60;\"><b>long<\/b><\/span><span style=\"color: #ff80ff;\">)\r\n(<\/span><span style=\"color: #ffa0a0;\">1000000.0<\/span><span style=\"color: #ff80ff;\">\u00a0* php_combined_lcg(TSRMLS_C))))<\/span>\r\n<span style=\"color: #ff80ff;\">#else<\/span>\r\n<span style=\"color: #ff80ff;\">#define GENERATE_SEED() (((<\/span><span style=\"color: #60ff60;\"><b>long<\/b><\/span><span style=\"color: #ff80ff;\">) (time(<\/span><span style=\"color: #ffa0a0;\">0<\/span><span style=\"color: #ff80ff;\">) * getpid())) ^ \r\n\u00a0\u00a0\u00a0\u00a0\u00a0((<\/span><span style=\"color: #60ff60;\"><b>long<\/b><\/span><span style=\"color: #ff80ff;\">) (<\/span><span style=\"color: #ffa0a0;\">1000000.0<\/span><span style=\"color: #ff80ff;\">\u00a0* php_combined_lcg(TSRMLS_C))))<\/span>\r\n<span style=\"color: #ff80ff;\">#endif<\/span>\r\n<\/pre>\n<p>So it&#8217;s seeded with the current time() (known), process id (weak), and php_combined_lcg(). What the heck is php_combined_lcg? Well, an LCG is a Linear Congruential Generator, a type of random number generator, and it&#8217;s defined at ext\/standard\/lcg.c so let&#8217;s take a look:<\/p>\n<pre>PHPAPI <span style=\"color: #60ff60;\"><b>double<\/b><\/span>\u00a0php_combined_lcg(TSRMLS_D) <span style=\"color: #80a0ff;\">\/*<\/span><span style=\"color: #80a0ff;\">\u00a0{{{ <\/span><span style=\"color: #80a0ff;\">*\/<\/span>\r\n{\r\n\u00a0\u00a0\u00a0\u00a0php_int32 q;\r\n\u00a0\u00a0\u00a0\u00a0php_int32 z;\r\n\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0(!LCG(seeded)) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0lcg_seed(TSRMLS_C);\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\r\n\u00a0\u00a0\u00a0\u00a0MODMULT(<span style=\"color: #ffa0a0;\">53668<\/span>, <span style=\"color: #ffa0a0;\">40014<\/span>, <span style=\"color: #ffa0a0;\">12211<\/span>, <span style=\"color: #ffa0a0;\">2147483563L<\/span>, LCG(s1));\r\n\u00a0\u00a0\u00a0\u00a0MODMULT(<span style=\"color: #ffa0a0;\">52774<\/span>, <span style=\"color: #ffa0a0;\">40692<\/span>, <span style=\"color: #ffa0a0;\">3791<\/span>, <span style=\"color: #ffa0a0;\">2147483399L<\/span>, LCG(s2));\r\n\r\n\u00a0\u00a0\u00a0\u00a0z = LCG(s1) - LCG(s2);\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0(z &lt; <span style=\"color: #ffa0a0;\">1<\/span>) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0z += <span style=\"color: #ffa0a0;\">2147483562<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>return<\/b><\/span>\u00a0z * <span style=\"color: #ffa0a0;\">4.656613e-10<\/span>;\r\n}\r\n<\/pre>\n<p>This function also needs to be seeded! It&#8217;s pretty funny to seed a random number generator with another random number generator &#8211; what, exactly, does that improve?<\/p>\n<p>Here is what lcg_seed(), in the same file, looks like:<\/p>\n<pre><span style=\"color: #60ff60;\"><b>static<\/b><\/span>\u00a0<span style=\"color: #60ff60;\"><b>void<\/b><\/span>\u00a0lcg_seed(TSRMLS_D) <span style=\"color: #80a0ff;\">\/*<\/span><span style=\"color: #80a0ff;\">\u00a0{{{ <\/span><span style=\"color: #80a0ff;\">*\/<\/span>\r\n{\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #60ff60;\"><b>struct<\/b><\/span>\u00a0timeval tv;\r\n\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0(gettimeofday(&amp;tv, <span style=\"color: #ffa0a0;\">NULL<\/span>) == <span style=\"color: #ffa0a0;\">0<\/span>) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0LCG(s1) = tv.tv_sec ^ (tv.tv_usec&lt;&lt;<span style=\"color: #ffa0a0;\">11<\/span>);\r\n\u00a0\u00a0\u00a0\u00a0} <span style=\"color: #ffff60;\"><b>else<\/b><\/span>\u00a0{\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0LCG(s1) = <span style=\"color: #ffa0a0;\">1<\/span>;\r\n\u00a0\u00a0\u00a0\u00a0}\r\n<span style=\"color: #ff80ff;\">#ifdef ZTS<\/span>\r\n\u00a0\u00a0\u00a0\u00a0LCG(s2) = (<span style=\"color: #60ff60;\"><b>long<\/b><\/span>) tsrm_thread_id();\r\n<span style=\"color: #ff80ff;\">#else<\/span>\u00a0\r\n\u00a0\u00a0\u00a0\u00a0LCG(s2) = (<span style=\"color: #60ff60;\"><b>long<\/b><\/span>) getpid();\r\n<span style=\"color: #ff80ff;\">#endif<\/span>\r\n\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #80a0ff;\">\/*<\/span><span style=\"color: #80a0ff;\">\u00a0Add entropy to s2 by calling gettimeofday() again <\/span><span style=\"color: #80a0ff;\">*\/<\/span>\r\n\u00a0\u00a0\u00a0\u00a0<span style=\"color: #ffff60;\"><b>if<\/b><\/span>\u00a0(gettimeofday(&amp;tv, <span style=\"color: #ffa0a0;\">NULL<\/span>) == <span style=\"color: #ffa0a0;\">0<\/span>) {\r\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0LCG(s2) ^= (tv.tv_usec&lt;&lt;<span style=\"color: #ffa0a0;\">11<\/span>);\r\n\u00a0\u00a0\u00a0\u00a0}\r\n\r\n\u00a0\u00a0\u00a0\u00a0LCG(seeded) = <span style=\"color: #ffa0a0;\">1<\/span>;\r\n}<\/pre>\n<p>This is seeded with the current time (known), the process id (weak), and the current time again (still known).<\/p>\n<p>So to summarize, unless I&#8217;m missing something, PHP&#8217;s automatic seeding uses the following for entropy:<\/p>\n<ul>\n<li>Current time (known value)<\/li>\n<li>Process ID (predictable range)<\/li>\n<li>php_combined_lcg\n<ul>\n<li>Current time (again)<\/li>\n<li>Process id (again)<\/li>\n<li>Current time (yet again)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>I haven&#8217;t done any further research into PHP&#8217;s random number generator, but from what I&#8217;ve seen I don&#8217;t get a good feeling about it. It would be interesting if somebody took this a step further and actually wrote an attack against PHP&#8217;s random number implementation. That, or discovered a source of entropy that I was unaware of. Because, from the code I&#8217;ve looked at, it looks like there may be some problems.<\/p>\n<p>An additional issue is that every seed generated is cast to a (long), which is 32-bits. That means that at the very most, despite the ridiculously long period of the mt_rand() function, there are only 4.2 billion possible seeds. That means, at the very best, an application that relies entirely on mt_rand() or rand() for their randomness are going to be a lot less random than they think!<\/p>\n<p>It turns out, after a little research, I&#8217;m <a href=\"http:\/\/www.suspekt.org\/2008\/08\/17\/mt_srand-and-not-so-random-numbers\/\">not the only one<\/a> who&#8217;s noticed problems with PHP&#8217;s random functions. In fact, in that article, Stefan goes over a history of PHP&#8217;s random number issues. It turns out, what I&#8217;ve found is only the tip of the iceberg!<\/p>\n<h2>Observations<\/h2>\n<p>I hope the last three blogs have raised some awareness on how randomization can be used and abused. It turns out, using randomness is far more complex than people realize. First, you have to know how to use it properly; otherwise, you&#8217;ve already lost. Second, you have to consider how you&#8217;re generating the it in the first place.<\/p>\n<p>It seems that the vast majority of applications make either one mistake or the other. It&#8217;s difficult to create &#8220;good&#8221; randomness, though, and I think the one that does the best job is actually SMF.<\/p>\n<h2>Recommendation<\/h2>\n<p>Here is what I would suggest:<\/p>\n<ul>\n<li>Get your randomness from multiple sources<\/li>\n<li>Save a good random seed between sessions (eg, save the last output of the random number generator to the database)<\/li>\n<li>Use cryptographically secure functions for random generation (for example, hashing functions)<\/li>\n<li>Don&#8217;t limit your seeds to 32-bit values<\/li>\n<li>Collect entropy in the application, if possible (what happens in your application that is impossible to guess\/detect\/force but that can accumulate?)<\/li>\n<\/ul>\n<p>I&#8217;m sure there are some other great suggestions for ensuring your random numbers are cryptographically secure, and I&#8217;ve love to hear them!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is part 3 to my 2-part series on password reset attacks (Part 1 \/ Part 2). Overall, I got awesome feedback on the first two parts, but I got the same question over and over: what&#8217;s the RIGHT way to do this? So, here&#8217;s the thing. I like to break stuff, but I generally &hellip; <a href=\"https:\/\/piratesecurityblog.com\/?p=10\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">(Mostly) good password resets<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[2],"tags":[6],"_links":{"self":[{"href":"https:\/\/piratesecurityblog.com\/index.php?rest_route=\/wp\/v2\/posts\/10"}],"collection":[{"href":"https:\/\/piratesecurityblog.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/piratesecurityblog.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/piratesecurityblog.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/piratesecurityblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10"}],"version-history":[{"count":1,"href":"https:\/\/piratesecurityblog.com\/index.php?rest_route=\/wp\/v2\/posts\/10\/revisions"}],"predecessor-version":[{"id":11,"href":"https:\/\/piratesecurityblog.com\/index.php?rest_route=\/wp\/v2\/posts\/10\/revisions\/11"}],"wp:attachment":[{"href":"https:\/\/piratesecurityblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/piratesecurityblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/piratesecurityblog.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}