• 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中分析如何实现双向的加密/解密操作,来克服上面提出的问题。

     

    References:

    1, MSDN, Duwamish 7.0

    2, Rickie, Duwamish密码分析篇, Part 1

  • 相关阅读:
    CFA 投资学 6.有效性市场假说 EMH Efficient Market Hypothesis
    CFA 投资学 1.投资学背景知识
    CFA 投资学 3.资产配置 separation property
    Kafka KafkaKraft模式
    量化 多因子策略3 : 回测选股
    Kafka KafkaEagle(EFAK)监控
    CFA 财务分析与估值 5.对企业价值的再认识
    CFA 投资学 2.股权的风险度量:index编纂,二次效用方程,VAR
    CFA 财务分析与估值 4.乘数法估值
    CFA 投资学 4.资本资产定价理论CAPM Capital Asset Pricing Model
  • 原文地址:https://www.cnblogs.com/rickie/p/61153.html
Copyright © 2020-2023  润新知