Duwamish密碼分析篇, Part 2

Duwamish密碼分析篇, Part 2

 

 

繼續前面關於DuwamishPOST,這裏將學習Duwamish中關於Password的處理方式。Duwamish 7.0範例中的帳戶密碼通過SHA1散列運算和對散列執行Salt運算後,是以byte形式存放在Database中,避免明文的方式,以提高系統的安全性。

 

1,【用戶登錄】過程概述

Web 層中啓動登錄過程。用戶輸入電子郵件地址和密碼(憑據),然後單擊“Logon”(登錄)按鈕,這將調用 Duwamish7.Web.Logon.LogonButton_Click 方法。下一步,Duwamish7.Web.Logon.LogonButton_Click 方法創建密碼的散列表示形式,並將憑據傳遞給業務外觀層的 Duwamish7.BusinessFacade.CustomerSystem.GetCustomerByEmail 方法。接着 Duwamish7.DataAccess.Customers.LoadCustomerByEmail 方法調用數據訪問層,後者又調用 GetCustomerByEmail 存儲過程 (SPROC)。然後通過 ComparePasswords 方法,相對於從數據庫中檢索的經過 salt 和散列運算的密碼來驗證散列密碼。如果憑據有效,則客戶帳戶信息成功地存儲到 Cart 對象,並且 ASP.NET Forms 身份驗證通過 pageBase ShoppingCart.Customer() 屬性驗證憑據。如果憑據無效,則 MismatchLabel 設置爲可見,它在 ASP.NET 頁上顯示下面的內容:“Invalid email address or password- please try again”(電子郵件地址或密碼無效,請再試一次)。

 

2,下面看看【用戶登錄】驗證功能模塊具體的實現代碼

1Duwamish7.Web.Logon.LogonButton_Click 方法

該方法首先創建用戶輸入密碼的SHA1散列形式,然後調用業務外觀層的 Duwamish7.BusinessFacade.CustomerSystem.GetCustomerByEmail 方法。

    //

    // Check the Email and Password combination

    //

    SHA1 sha1 = SHA1.Create();

    byte [] password = sha1.ComputeHash(Encoding.Unicode.GetBytes(LogonPasswordTextBox.Text));

 

    custData = (new CustomerSystem()).GetCustomerByEmail(LogonEmailTextBox.Text, password);

   

    if (custData != null)   //were they valid?

    {

        //

        // 1. Update customer in session.

        // 2. Update customer in cart.

        //

        base.Customer = custData;

        base.ShoppingCart().Customer = custData;

        // 將已驗證身份的用戶重定向回最初請求的 URL

        FormsAuthentication.RedirectFromLoginPage("*", false);

    }

    else

    {

        MismatchLabel.Visible = true;

    }

 

如果憑據有效,則客戶帳戶信息成功地存儲到 Cart 對象,並且 ASP.NET Forms 身份驗證通過 pageBase ShoppingCart.Customer() 屬性驗證憑據。如果憑據無效,則 MismatchLabel 設置爲可見,它在 ASP.NET 頁上顯示下面的內容:“Invalid email address or password- please try again”(電子郵件地址或密碼無效,請再試一次)

 

2)業務外觀層的 CustomerSystem.GetCustomerByEmail 方法

根據用戶的email,獲取DatabaseCustomerPassword,該Password已執行散列運算和對散列執行過Salt運算,是24個字節長度的byte數組。

public CustomerData GetCustomerByEmail(String emailAddress, byte [] password)

{

      //

      // Check preconditions

      //

      ApplicationAssert.CheckCondition(emailAddress != String.Empty, "Email address is required", ApplicationAssert.LineNumber);

      ApplicationAssert.CheckCondition(password.Length != 0, "Password is required", ApplicationAssert.LineNumber);

      //

      // Get the customer dataSet

      //

      CustomerData dataSet;

      using (DataAccess.Customers customersDataAccess = new DataAccess.Customers())

      {

           dataSet = customersDataAccess.LoadCustomerByEmail(emailAddress);

      }

      //   

      // Verify the customer's password

      //

      DataRowCollection rows = dataSet.Tables[CustomerData.CUSTOMERS_TABLE].Rows;

 

      if ( ( rows.Count == 1 ))

      {

           byte [] dbPassword = (byte[])rows[0][CustomerData.PASSWORD_FIELD];

 

           if (ComparePasswords (dbPassword, password))

                 return dataSet;

           else

                 return null;

      }

      else

           return null;

}

 

在獲取到DataAccess層返回的CustomerData對象後,進一步調用類中的私有方法ComparePasswords()。該方法負責從Password字段值中提取Salt值,然後運用該Salt值對傳入的SHA1散列執行Salt運算。

 

其中CreateSaltedPassword()方法和前面【用戶註冊】過程相同,用來對散列結果再次執行Salt運算。

// compare the hashed password against the stored password

private bool ComparePasswords(byte[] storedPassword, byte[] hashedPassword)

{

      if (storedPassword == null || hashedPassword == null || hashedPassword.Length != storedPassword.Length - saltLength)

           return false;

 

      // get the saved saltValue

      // 獲取已保存在password數據字段中的Salt

      byte[] saltValue = new byte[saltLength];

      int saltOffset = storedPassword.Length - saltLength;

      for (int i = 0; i < saltLength; i++)

           saltValue[i] = storedPassword[saltOffset + i];

 

      byte[] saltedPassword = CreateSaltedPassword(saltValue, hashedPassword);

 

      // compare the values

      return CompareByteArray(storedPassword, saltedPassword);

}

 

按字節比較兩個字節數組是否相等,分別傳入數據表中Password字段byte數組和對當前散列執行Salt運算後的byte數組。

// compare the contents of two byte arrays

private bool CompareByteArray(byte[] array1, byte[] array2)

{

      if (array1.Length != array2.Length)

           return false;

      for (int i = 0; i < array1.Length; i++)

      {

           if (array1[i] != array2[i])

                 return false;

      }

      return true;

}

 

3)數據訪問層的Customers.LoadCustomerByEmail()方法

該方法根據傳入的emailAddress參數,查詢Database,返回CustomerData對象。過程比較簡單,詳細代碼請查詢Duwamish 7.0範例。

 

3Summary

通過對Duwamish 7.0範例中用戶註冊和用戶登錄驗證過程的分析,我們確信用戶Password以安全的Salt運算結果存放在後臺的Database中。

其實,在實際的應用系統中,上述的加密過程有一個小問題:就是當有大量的用戶忘記了自己的Password,如何幫助他們恢復自己的Password呢?這個問題地球人都不知道,只有通過另外的application來將這些Password重新Update爲新的Password,因爲散列是單向操作,使用散列算法對原始密碼加密後將無法再恢復。

 

因此,將在Duwamish密碼分析篇, Part 3中分析如何實現雙向的加密/解密操作,來克服上面提出的問題。 

 

 

轉自:http://www.cnblogs.com/rickie/archive/2004/11/07/61153.html

發佈了17 篇原創文章 · 獲贊 0 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章