2010/07/30

[ASP.Net] Regular Expression 的密碼驗證

其實用 Regular Expression 做密碼複雜度的驗證已不是什麼創舉, 用 google 輸入關鍵字 "regular expression password" 就會有一堆參考.
由於 (?=) 和 (?!) 的用法之前有在 這篇文章 大略提過, 所以此篇就當做是為了避免自己忘掉, 且做為衍生應用寫的吧.

關於密碼驗證的 Regular Expression, 我自己在網路上找到的是以下這個:
^.*(?=.{10,})(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[@#$%^&+=]).*$
上述的 Regular Expression 可達到以下幾個驗證密碼的項目:
  1. (?=.{10,}): 密碼長度 10 個以上
  2. (?=.*\d): 至少要有一個 0-9 的數字
  3. (?=.*[a-z]): 至少要有一個小寫的英文字母 (a-z)
  4. (?=.*[A-Z]]): 至少要有一個大寫的英文字母 (A-Z)
  5. (?=.*[@#$%^&+=]): 至少要有清單中的一個特殊符號 (@#$%^&+=)
要看懂以上的 Regular Expression, 可以到 Wikipedia 的 Syntax 章節參考一下.
技巧上很簡單, 把每個 (?=) 項目都當做個驗證的關卡來看, 要通過這個密碼驗證, 就要每一關都通過.
至於整個 Regular Expression 最後面的 .* 是一定要有的, 前面的 .* 可有可無.如果兩個都沒有, 或是只有前面有 .*, 都是不行的. (有興趣的可以多方嘗試)
以下說明一下各個驗證的項目: (記住: 不管是 (?=)(?!), .* 都記得先放進去)
  • (?=.{10,}): 檢查輸入的字元長度是否有超過 10. 如果資料庫裡的密碼欄位長度是 20, 那就改成 (?=.{10,20}).
  • (?=.*\d): 因為 \d 表示 [0-9], 所以此部分是檢查輸入的字串裡有沒有 0-9 的數字.
  • (?=.*[a-z])(?=.*[A-Z]): 概念同 (?=.*\d), 不過這是分開檢查有沒有輸入大寫與小寫英文字母. 如果只是要有英文字母, 就改成 (?=.*[a-zA-Z]).
  • (?=.*[@#$%^&+=]): 要使用者輸入至少一個指定的特殊符號. 有人會用 (?=.*\W) 來表示,因為 \W 表示 [^A-Za-z0-9_],這邊要注意 \W 有排除掉 '_' 這個符號.
上述的密碼驗證在大部分情況都適用, 不過要找碴的話, 就是這個 Regular Expression 允許輸入雙字元.
由於大多數的程式設計師不會將密碼欄位的資料型態訂成 nvarchar, 所以我們可以再多加一個不允許雙字元輸入的判斷, 以避免真的有人來亂的.
這時候就要參考一下 ASCII 字元表, 查一下要排除的字元有哪些.
如果只是要排除雙字元, 那麼就多加一個以下的判斷:
(?!.*[^\x00-\xff]): 表示不允許輸入 ASCII 以外的字元.

不過, 也許有人發現, ASCII 字元表中, 也不是所有的字元都應該要接受.
至少我認為 \x00 到 \x20, 以及 \x7F 到 \xFF 這個範圍內的都不需要, 只有 \x21 到 \x7E 的部分是可接受輸入的.
所以可以再修改一下上面僅允許 ASCII 的驗證, 改成如下的驗證:
(?!.*[^\x21-\x7e]): 表示不允許輸入 \x21 到 \x7E 以外的字元.

最後, 我密碼驗證的 Regular Expression 如下:
^(?!.*[^\x21-\x7e])(?=.{4,10})(?=.*[\W])(?=.*[a-zA-Z])(?=.*\d).*$
上述的 Regular Expression 可達到以下幾個驗證密碼的項目 (依序):
  1. 不允許特殊符號, 數字, 英文字母以外的字元輸入
  2. 密碼長度 4 到 10 個字元
  3. 至少要有一個特殊符號
  4. 至少要有一個大寫或小寫的英文字母
  5. 至少要有一個 0-9 的數字
上面所列的 Regular Expression, 可以用在 RegularExpressionValidator 的 ValidationExpression 屬性.
不過要注意的是, 在 Web 用的 Regular Expression 跟透過 C# 或 VB 的 Regex 物件做出的判斷會有不一樣的地方.
例如,將長度驗證的部分拿到 (?=) 以外的地方去做, 如下例:
^(?!.*[^\x21-\x7e])(?=.*[\W])(?=.*[a-zA-Z])(?=.*\d).{4,10}$
在 RegularExpressionValidator 中, 輸入 "111!!@A" 會驗證不過, 但改成 "111!!@A@1A" 卻又可驗證過.
而在 C# 的程式中, 以下兩個都會回傳 True:
Regex regex = new Regex(
@"^(?!.*[^\x21-\x7e])(?=.*[\W])(?=.*[a-zA-Z])(?=.*\d).{4,10}$");
Console.WriteLine(regex.IsMatch("111!!@A"));
Console.WriteLine(regex.IsMatch("111!!@A@1A"));
所以, 個人建議將所有的判斷都寫在 (?=)(?!) 中.

沒有留言: