[小菜一碟] 在 SQL Server 做密碼的雜湊跟比對

我們都知道不應該將密碼以明碼的方式存進資料庫,至少密碼要雜湊(Hash)過才放進資料庫,大多數的人應該都是選擇在應用程式裡面,先將密碼雜湊過,再將密碼存進資料庫內,不過 SQL Server 已經有一套機制來處理密碼雜湊這件事,我們可以放心地交給 SQL Server 自己來處理。

PWDENCRYPT() + PWDCOMPARE()

第一種方式我們可以用 PWDENCRYPT()PWDCOMPARE() 函式來處理,它們是一對的,一個用來雜湊,一個用來比對,使用方式也很簡單,首先我們要先將密碼欄位的型態宣告成 VARBINARY(MAX),因為雜湊出來的結果是二進位的資料。

接著在使用者設定密碼的時候,我們將密碼透過 PWDENCRYPT() 函式雜湊後存進資料庫。

使用者要登入的時候,將輸入的密碼跟雜湊後的密碼傳入 PWDCOMPARE() 函式進行比對,結果是 1 就是相同,結果是 0 就是不同,藉此來判斷使用者輸入的密碼正不正確?

這邊要特別注意一件事情,就是 PWDCOMPARE() 函式的第一個參數有指定型別 NVARCHAR(128),所以我們要特別注意原始密碼參數的型別也要是 NVARCHAR。

不過我們去看官網關於 PWDENCRYPT() 函式的說明時,會看到下面這段話:

PWDENCRYPT 是比較舊的函數,在 SQL Server 的未來版本中可能不支援。 請改為使用 HASHBYTES。 HASHBYTES 提供更多雜湊演算法。

官網建議我們改用 HASHBYTES() 函式來做雜湊,所以我們接下來改用 HASHBYTES() 函式來撰寫密碼雜湊跟比對的邏輯。

HASHBYTES() + PWDCOMPARE()

HASHBYTES() 函式的使用方式需要傳入兩個參數:雜湊演算法雜湊的資料,HASHBYTES() 支援的雜湊演算法有 MD2MD4MD5SHASHA1SHA2_256SHA2_512,雖然支援的雜湊演算法不少,但是官網建議用 SHA2_256 或 SHA2_512 就好了。

接著我們就依樣畫葫蘆改用 HASHBYTES() 來雜湊密碼,然後我們就會發現,永遠撈不出 Member 資料,因為 PWDCOMPARE() 函式回傳的比對結果永遠不等於 1。

怎麼會這樣? 因為其實 PWDENCRYPT() 及 PWDCOMPARE() 兩個函式之間是有特別對雜湊結果制定一個格式,而這個格式我們在官網查不太到,不過國外 SQL 界的大大有找出格式:

{Version} | {4 Bytes Salt} | {HASHBYTES('HASH_ALGORITHM', Binary Password + 4 Bytes Salt )}

所以,我們需要按照這個格式去產生密碼的雜湊結果,這樣 PWDCOMPARE() 函式才能正常運作。

我們就來說明一下這個格式:

  • {Version}:用來宣告使用的雜湊演算法,0X0200 就是宣告說使用 SHA2 的雜湊演算法。
  • {4 Bytes Salt}:4 個 Binary 單位的 Salt,它必須接在密碼的後面一起進行雜湊。

如果我們的資料庫是從 SQL Server 2000 這樣一路升級上來的,在不知道使用者密碼的狀況之下,我們是不可能用最新版的雜湊邏輯重新產生密碼的雜湊結果去更新它的,這時候我們就要調整 Version 跟 HASH_ALGORITHM 參數,改用舊版雜湊邏輯產生密碼的雜湊結果,這樣我們才能保持邏輯一致。

保護使用者密碼,在現在資安觀念有所提升的時代,應該是最基本要做的事情,保護密碼的方式有很多種,至少我們選一種來做,盡量避免再使用明碼來儲存密碼。

參考資料

C# 指南ASP.NET 教學ASP.NET MVC 指引
Azure SQL Database 教學SQL Server 教學Xamarin.Forms 教學