Strong password generator
Jul 16th, 2008 by knutee
I’ve been working on a password generator written in PHP the last 2 days, and now I’ve finally finished it. As with many projects, you start out thinking “I’ll just do this, then that, and it will work”. However, as it often turns out there are things you forgot to think about. I’ll mention these obstacles as I explain the code.
As I mention in my previous post, I had a set of requirements for the password generator:
- All alphanumeric characters
- A set of special characters
- A scale of strength, defining which characters to use
- Repeated generation until length of password is met
- Random choice of characters to use
Now, lets get started!
I chose to write most of the HTML directly, instead of having it printed out by PHP. It does not really matter which way you do it, thought in this case I could have made some things a bit easier by going that route. So, first of I set the HTML tags and defined my generating function.
-
<html>
-
<body>
-
<?php
-
function genPass($length, $strength) {
genPass() accepts two arguments, the length and strength of the password. Next up I needed to add input validation, to avoid bad input causing unwanted behavior or errors.
-
if($length > 0 && $length < 257) {
This IF statment ensures that no one can request a password of less than 1 and more than 256 in length. It also prevents character input from being processed.
-
$lowerCase = ‘abcdefghijklmnopqrstuvwxyz’;
-
$upperCase = ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’
-
$numbers = ‘0123456789′;
-
$specialChars = ‘!@#$%&=+?.,’;
-
$password = ”;
The above code shows the variables and arrays I needed to define, as well as a few control routines. The first 5 lines are fairly straight forward. Four arrays of characters are defined and the password is set to blank. $caseNr defines how many times the upper- and lowercase arrays can be used, and $charNr does the same for numbers and special characters. This is where I ran in to my first error. The original code had $charNr set to floor($length/($strength)), which meant the result would always be rounded down. This works with some numbers, but what happens when you set length to 15 and strength to 4?
15/4 = 3.75 => $caseNr = 4 & $charNr = 3
The result is (4*2)+(3*2)=14. And thereby (as you will see later) the generator function gets sent into a never ending loop, only stopped by Apache’s 30 second timer. The solution was to use normal rounding on the result.
Lastly, the $control array is created and all values are set to 0. This array will keep track of how many times a character array is used.
-
for ($i = 0; $i < $length; ) {
-
// Character selection code will go here
-
}
Here I create the shell for the loop that will put characters into the password array until the length and the rules for strength and randomness is met. rand() will pick a number between 1 and the strength for each run, until the password is complete. So lets fill in the blank with some code!
-
if ($selection == 1 && $control[$selection] < $caseNr) {
-
$control[$selection]++; $i++;
-
}
-
elseif ($selection == 2 && $control[$selection] < $caseNr) {
-
$control[$selection]++; $i++;
-
}
-
elseif ($selection == 3 && $control[$selection] < $charNr) {
-
$control[$selection]++; $i++;
-
}
-
elseif ($selection == 4 && $control[$selection] < $charNr) {
-
$control[$selection]++; $i++;
-
}
This part should be fairly easy to understand. After rand() has picked a number (1-strength), the corresponding IF statement checks if that character class has been used too many times. If it hasn’t, a randomly selected character from that character class’s array is picked and added to the password. $i and the correct cell in the control array is increased by one.
Combined with the FOR loop posted above, this will generate a random string of characters and numbers. The control array makes sure that every wanted character array is used.
Lastly, we need to make sure the password array is converted to a text string and that this string is returned.
-
return $passString;
-
}
-
}
-
?>
Thats the function done. Now we need to create a form that lets you choose the options you want, and allow you to generate your passwords. We’ll let the form call itself. This is one of the parts I struggled a bit with. As the form calls $PHP_SELF I had to find ways to store the previous selection and output the correct password.
I’ve removed the layout part of the code.
-
<form action="<?php $PHP_SELF; ?>" method="post">
Then the length input needs to be created. Remember that I’m writing the HTML directly, and not my calling echo or print. How you do this is up to you.
-
Length <input type="text" name="length" value="
-
<?php if($_POST['length']) { echo $_POST['length']; } else { echo ‘8′; } ?>
-
" size="3" />
The input field is created like normal, but here I insert PHP code into the value field. What this does is check what your previous selection was and reuse it. If there was no previous selection, it defaults to 8.
-
Strength <select name="strength">
-
</select>
-
I basically do the same with the strength selector. The only difference is that I use a drop down box to simplify the UI. The selector is created when the page is fetched, so you have to acquire the previous selection and use it to set the selected argument.
I intend to make this part a lot cleaner by using an array and the FOREACH loop. The guide will be updated when this is done.
-
<input type="text" name="password" value="
-
<?php if($_POST['strength'] && $_POST['length']) { print genPass($_POST['length'], $_POST['strength']); } ?>
-
" size="12" />
I make another input box, but this time I use it to output the generated password by calling the genPass function with the selected length and strength values. The IF statement prevents it from trying to generate a password the first time you open the page.
Lastly, we need to close it all up with a button and the last HTML code.
-
<input type="submit" value="Generate" />
-
</form>
-
</body>
-
</html>
So there you go! A working strong password generator than you can have on your site or intraweb. The finished result can be seen here.
As always, feedback and comments are very welcome!
This is great. I’ve looked for a good way to ensure secure random passwords and this one does exactly what I need it to do.