Scott Hanselman

Dictionary Password Generator in Powershell

July 26, '06 Comments [6] Posted in Programming
Sponsored By

A fellow at work wanted a script to generate strong passwords from a dictionary file. The "l337" hashtable in the first line is the list of characters that we'll be replacing. The $skip variable adds a little variance so we won't (likely) generate the same password twice given the same word.

$leet = @{e=3;o=0;l=1;a=4;i='!';t=7}
$rand = new-object System.Random
$words = import-csv dict.csv
$word = ($words[$rand.Next(0,$words.Count)]).Word
if ($args[0]) { $word = $args[0] }
$leet.Keys | foreach-object { $skip = $rand.Next(0,3); if ($skip -ne 0){ $word = $word.Replace($_,$leet[$_]) } }
$word

Here's the dictionary I used: File Attachment: dict.csv (6 KB)

> ./generate-password.ps1
1ass!7ude
!nterd!ct
c4j013
par!ah
ruff!4n
r3cant
pl3th0r4
> ./generate-password.ps1 "quixotic"
qu!x07!c
quixo7ic
quix0tic

UPDATE: Jeff opines (correctly) passphrases are where it's at, so...

$rand = new-object System.Random
$conjunction = "the","my","we","our","and","but","+"
$words = import-csv dict.csv
$word1 = ($words[$rand.Next(0,$words.Count)]).Word
$con = ($conjunction[$rand.Next(0,$conjunction.Count)])
$word2 = ($words[$rand.Next(0,$words.Count)]).Word
return $word1 + " " + $con + " " + $word2

Yielding:

> ./generate-passphrase.ps1'
foible my finesse
bolster but permeate
augury my dogmatic
surfeit the mercurial
reconcile but dexterity
acarpous our inveigh
perilous and bequest
cognizant we foible
calipers and vilify
trickle our enzyme
vigorous we ominous
ascertain + dubious
suborn but middling

I totally agree that passphrases are better. I've advocated them to family and friends and have a few long-ass passphrases myselfs

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Wednesday, July 26, 2006 6:30:29 PM UTC
I'm not sure the "leet" rules are that much harder to break than a regular dictionary word, particularly since there are well-understood rules for "leet"-ing stuff (e=3, i=1, etc).

I wish more users would adopt passphrases:

http://www.codinghorror.com/blog/archives/000360.html

Perhaps you can code an example of powershell building a much more secure *AND* easier to remember passphrase from that same dictionary?
Wednesday, July 26, 2006 6:47:20 PM UTC
Jeff, I was getting ready to make the same comment regarding lee7 algorithms.

A great suggestion that Jeff Gibson made in the Security Now! podcast is to invent your own personal password algorithm that can be applied to a variety of sources. Something like "1" + [the first three letters of the site in reverse order] + "24! + [capitalized last letter of the site]. You won't have to remember every different password, just the algorithm, and its invulnerable to dictionary attacks.

I adopted this type of strategy about 6 months ago, and my passwords are not only much stronger, but I don't need to store them.
Dave P.
Wednesday, July 26, 2006 9:05:09 PM UTC
Seeing the original script, three things jumped out at me.

1) If the stars aligned and a character was being replaced with its 1337 equivilent, it was replaced throughout the entire password. While probably easier to remember, it's a level of predictability that I'm not comfortable with in a password generator.

2) A number of words in the dictionary are shorter than 8 characters. Many systems require 8 characters minimum.

3) Even though I'm a 1337 633|< myself, it sometimes takes me a second to realize what the 1337 word is supposed to be.

So I took the liberty of addressing these issues.

$leet = @{e=3;o=0;l=1;a=4;i='!';t=7}
$paddingchars = "abcdefghijklmnopqrstuvwxyz01234567890".ToCharArray()
$rand = new-object System.Random
$words = import-csv dict.csv
$word = ($words[$rand.Next(0,$words.Count)]).Word
if ($args[0]) { $word = $args[0] }
$outputchars = $word.ToCharArray()
$outputchars | foreach-object { $i=0 }{ $skip = $rand.Next(0,3); if ( $skip -ne 0 -AND $leet.ContainsKey([string]$_) ) { $outputchars[$i] = [char][string]$leet[[string]$_]}; $i+=1 }
while ($outputchars.Length -lt 8) { $outputchars += $paddingchars[$rand.Next(0,$paddingchars.Length)]}
$OFS = ""
$originalword = $word
$word = [string]$outputchars
"Generated password: $word (based on '$originalword')"

(gee, I hope that displays okay as a dasBlog comment)

A couple of other things I would have done if I didn't actually have to get some work done today:

- random capitalization
- passing of minimum length as a parameter (rather than hard-coded as 8)

Of course, I'm a day late and a dollar short, since Jeff correctly pointed out that passphrases are a better solution.
Wednesday, July 26, 2006 9:06:41 PM UTC
I should have realized line 8 was too long. Either concantenate lines 8 and 9 or add a line continuation character to 8 and it should be fine.
Wednesday, July 26, 2006 9:33:50 PM UTC
A little off topic, but every looked at RoboForm (a la Pass2Go) ??? Excellent password manager that generates random passwords for you, and the bets thing, remembers and enteres them for you.

Awesome tool, maybe you'll add it to your Must Have tools list. Check it out. No, I don't work for them, just a happy user. It's free for up to 10 passwords too!

BOb
Bob Archer
Thursday, July 27, 2006 5:02:34 PM UTC
Passphrases may be where it's at, but I'm not sure I want anyone to foible my finesse.
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.