さらに強力なランダムパスワードジェネレータ
GUIDを使ってランダムなパスワードを生成する方法の良いところは、実現するのも使用するのも簡単なことです。しかし出力される結果をコントロールできない上に、英数字(しかも文字はaからfまで)のパスワードしか生成することができません。セキュリティのガイドラインを適用するために、アルファベット以外の文字を何文字か使用したパスワードや、広い範囲の文字(アルファベット全部など)を使ったパスワードなどが必要になったとしても、対応することができません。また、何らかの理由で数字を使わないパスワードが必要になった場合や、パスワードの大文字と小文字を区別したくなった場合にも同じことが言えます(前述のGUIDを使った方法では6文字しか使えず、しかもすべて小文字です)。
より堅牢な方法を実現するには、GeneratePassword(length, numberOfNonAlphanumericCharacters)というパスワードジェネレータを作ります。このパスワードジェネレータでは、ASP.NET 2.0のMembership APIに含まれている同名のメソッドと同等の機能を再現します(このメソッドは、ASP.NET 2.0を使っているのであればぜひ使いたいメソッドであり、パスワードの長さとアルファベット以外の文字の数を指定することができます)。さらに、このパスワードジェネレータは、強力な暗号化機能を持った乱数ジェネレータであるRNGCryptoServiceProviderクラスを使用しています。そのため、このアルゴリズムでは、以前に生成された乱数を基にこれから生成される乱数の予想を立てることは困難です。
私と同じようにASP.NET 1.xの世界から抜け出せないのであれば、自作のGeneratePassword()メソッドを使わなければなりません。しかし、ASP.NET 2.0と同じものを開発する必要はありません。ASP.NET 2.0のコードを借用すればよいのです。それはReflectorを使えば簡単に実現できます。Membership.GeneratePassword(length, numberOfNonAlphanumericCharacters)のコードを次に示します。ライブデモもご覧ください。
' VB.NET Function GeneratePassword(ByVal length As Integer, _ ByVal numberOfNonAlphanumericCharacters As Integer) As String 'Make sure length and numberOfNonAlphanumericCharacters are valid.... '... checks omitted for brevity ... see live demo for full code ... Do While True Dim i As Integer Dim nonANcount As Integer = 0 Dim buffer1 As Byte() = New Byte(length - 1) {} 'chPassword contains the password's characters as it's built up Dim chPassword As Char() = New Char(length - 1) {} 'chPunctionations contains the list of 'legal non-alphanumeric characters Dim chPunctuations as Char() = _ "!@@$%^^*()_-+=[{]};:>|./?".ToCharArray() 'Get a cryptographically strong series of bytes Dim rng as _ New System.Security.Cryptography.RNGCryptoServiceProvider rng.GetBytes(buffer1) For i = 0 To length - 1 'Convert each byte into its representative character Dim rndChr As Integer = (buffer1(i) Mod 87) If (rndChr < 10) Then chPassword(i) = Convert.ToChar(Convert.ToUInt16(48 + rndChr)) Else If (rndChr < 36) Then chPassword(i) = _ Convert.ToChar(Convert.ToUInt16((65 + rndChr) - 10)) Else If (rndChr < 62) Then chPassword(i) = _ Convert.ToChar(Convert.ToUInt16((97 + rndChr) - 36)) Else chPassword(i) = chPunctuations(rndChr - 62) nonANcount += 1 End If End If End If Next If nonANcount < numberOfNonAlphanumericCharacters Then Dim rndNumber As New Random For i = 0 To (numberOfNonAlphanumericCharacters - nonANcount) - 1 Dim passwordPos As Integer Do passwordPos = rndNumber.Next(0, length) Loop While Not Char.IsLetterOrDigit(chPassword(passwordPos)) chPassword(passwordPos) = _ chPunctuations(rndNumber.Next(0, chPunctuations.Length)) Next End If Return New String(chPassword) Loop End Function
GetPassword()メソッドに変更を加えて、エラーを起こす可能性のある英数字以外の文字(#と&)を除外しました。ASP.NET 2.0では、ランダムに生成されたパスワードがクロスサイトスクリプティング攻撃に悪用されないようにチェックしており、<、#、および&をチェック対象にしています。このメソッドでは、こうしたチェックをしなくて済むように、単純にこれらの文字を除外しています。まとめ
この記事では、ASP.NETを使った2種類のランダムなパスワードの生成方法を取り上げました。1つ目の手法では、GUIDからハイフンを取り除いて最初のlength文字を取得しました。これは手早く簡単にランダムなパスワードを生成する方法です。しかし、使用可能な文字の種類がかなり限られている(aからf、および0から9)ので、複雑なことはできません。
もっと強力で洗練されたランダムパスワードジェネレータが、ASP.NET 2.0のMembership.GeneratePassword()メソッドです。ASP.NET 2.0を使っているのであれば、このメソッドを使うのがよいでしょう。しかし、まだASP.NET 1.xを使っている場合に、GUIDを使った手法よりも強力なパスワードジェネレータが必要なのであれば、自分で作成する必要があります。そこでReflectorを使ってASP.NET 2.0のGeneratePassword()メソッドを「コピー」する方法を説明しました。
ぜひ試してみてください。
