Duwamish密码分析篇, Part 1

Duwamish密码分析篇, Part 1

 

Written by: Rickie Lee

Nov. 05, 2004

 

继续前面关于DuwamishPOST,这里将学习Duwamish中关于Password的处理方式。Duwamish 7.0范例中的帐户密码通过SHA1散列运算和对散列执行Salt运算后,是以byte形式存放在Database中,避免明文的方式,以提高系统的安全性。

 

Duwamish的用户注册部分是封装在/web/modules/accountmodule.ascx用户控件内。随便提一下,Duwamish web tier中采用了大量的user control,并且所有的user control都继承/web/ModuleBase.cs 类,与web page继承PageBase.cs类相似,这种做法值得推荐。Duwamishuser control主要是封装一些相应的功能,模块化。这样不仅可以在本web项目内重用,而且以后维护也比较方便,如/web/modules/accountmodule.ascx user control就封装了用户注册部分的功能。

 

下面看看【用户注册】功能模块具体的实现代码(/web/modules/accountmodule.ascx):

1获取用户登记/注册password,并帐户密码执行散列运算。

byte [] bytePassword = null;

String tmpPassword = PasswordTextBox.Text;

 

if (tmpPassword == ConfirmPasswordTextBox.Text)

{

    SHA1 sha1 = SHA1.Create();

    bytePassword = sha1.ComputeHash(Encoding.Unicode.GetBytes(tmpPassword));

}

……

retVal = (new CustomerSystem()).CreateCustomer(EmailTextBox.Text,

                                           bytePassword,

                                           AcctNameTextBox.Text,

                                           AddressTextBox.Text,

                                           CountryTextBox.Text,

                                           PhoneTextBox.Text,

                                           FaxTextBox.Text,

                                           out moduleCustomerInfo);

 

先使用实现 160 SHA-1 标准的 System.Security.Cryptography 命名空间对密码进行散列运算。然后调用BusinessFacade/CustomerSystem类的CreateCustomer()方法。

 

知识点:

散列简介

散列(Hash)是一种单向算法,一旦数据被转换,将无法再获得其原始值。大多数开发人员使用数据库存储密码,如果密码直接以明文的形式存放在数据库中,则开发人员也能够看到这些密码,甚至包括用户的Credit Card信息。

不过,我们可以使用散列算法对密码进行加密,然后再将其存储在数据库中。用户输入密码后,可以再次使用散列算法对其进行转换,然后将其与存储在数据库中的散列进行比较。散列的特点之一是,即使原始数据只发生一个小小的改动,数据的散列也会发生非常大的变化。Rickie Ricky 这两个单词非常相似,但使用散列算法加密后的结果却相去甚远。你可能根本看不出二者之间有什么相似之处。

 

.NET 开发人员可以使用多种散列算法类。最常用的是 SHA1 MD5。下面我们看一下如何为Rickie这样的普通字符串生成散列,使任何人都无法识别它。

1)使用 SHA1 生成散列

通过如下的示例代码,来演示如何通过SHA1生成散列:

byte [] bytePassword = null;

string tmpPassword = txtPassword.Text.Trim();

 

// 创建新的加密服务提供程序对象

SHA1 sha1 = SHA1.Create();

// 将原始字符串转换成字节数组,然后计算散列,并返回一个字节数组

bytePassword = sha1.ComputeHash(Encoding.Unicode.GetBytes(tmpPassword));

// Releases all resources used by the System.Security.Cryptography.HashAlgorithm.

sha1.Clear();

// 返回散列值的 Base64 编码字符串

txtResults.Text = Convert.ToBase64String(bytePassword);

 

传递不同的字符串值来调用该例程,查看散列值的变化。例如,如果将字符串Rickie传递给该例程,输出结果:

v8ocXHBvlh4EqY/2HsJNH5XBVG0=

现在,将此过程中的输入值更改为Ricky。你将看到以下输出结果:

luQsSa61sB/7PT9piDx+OAGqCnI=

 

如此可见,输入字符串的一个小小变化就会产生完全不同的字符组合。这正是散列算法之所以有效的原因,它使我们很难找到输入字符串的规律,也很难根据加密后的字符弄清楚字符串原来的模样。

 

2)使用MD5也可以生成散列

通过如下的示例代码,来演示如何通过MD5生成散列:

byte [] bytePassword = null;

string tmpPassword = txtPassword.Text.Trim();

 

MD5 md5 = MD5.Create();

bytePassword = md5.ComputeHash(Encoding.Unicode.GetBytes(tmpPassword));

// Releases all resources used by the System.Security.Cryptography.HashAlgorithm.

md5.Clear();

txtResults.Text = Convert.ToBase64String(bytePassword);

 

输入RickieMD5散列算法的输出结果:

YUqR1JfNxrciyG0ixNj58A==

 

同样,加密后的字符串看起来也与原始输入相去甚远。这些散列算法对于创建没有任何意义的密码来说非常有用,也使黑客很难猜出这些密码。之所以使用散列算法,是因为可以用这种算法对密码进行加密并将其存储在数据库中。然后,当用户输入真实密码时,需要先对用户输入的密码进行同样的散列,然后通过网络发送到数据库中,比较它与数据库中的密码是否匹配。

 

请记住,散列是单向操作。使用散列算法对原始密码加密后将无法再恢复。

 

上述两种散列算法都执行同一种操作。不同之处只在于生成散列的密钥大小以及使用的算法。使用的密钥越大,加密就越安全。例如,MD5 使用的加密密钥比 SHA1 使用的密钥大,因此 MD5 散列较难破解。

 

对于散列算法要考虑的另外一点是,从实践或理论的角度上看是否存在冲突的可能性。冲突是我们所不希望的,因为两个不同的单词可能会生成相同的散列。例如,SHA1 从实践或理论上来讲没有发生冲突的可能性。MD5 从理论上讲有发生冲突的可能性,但从实践上讲没有发生冲突的可能性。因此,选择哪种算法归根结底取决于所需要的安全级别。

 

3Summary

一般情况下,将上述加密的字节数组,通过使用Convert.ToBase64String(bytePassword)方法把字节数组转换成 Base64 编码的字符串,然后存储在数据库中即可完成一般的商业应用。

 

 

2,调用BusinessFacade/CustomerSystem类,对散列执行Salt运算。

到目前为止,散列算法暴露出来的问题之一是,如果两个用户碰巧使用相同的密码,那么散列值将完全相同。如果黑客看到您存储密码的表格,会从中找到规律并明白您很可能使用了常见的词语,然后黑客会开始词典攻击以确定这些密码。要确保任何两个用户密码的散列值都不相同,一种方法是在加密密码之前,在每个用户的密码中添加一个唯一的值。这个唯一值称为“盐”值(Salt)。

 

虽然对密码执行散列运算是一个好的开端,但若要增加免受潜在攻击的安全性,则可以对密码散列执行 Salt 运算。Salt 就是在已执行散列运算的密码中插入的一个随机数字。这一策略有助于阻止潜在的攻击者利用预先计算的字典攻击。字典攻击是攻击者使用密钥的所有可能组合来破解密码的攻击。当您使用 Salt 值使散列运算进一步随机化后,攻击者将需要为每个 Salt 值创建一个字典,这将使攻击变得非常复杂且成本极高。

 

Salt 值随散列存储在一起,并且未经过加密。所存储的 Salt 值可以在随后用于密码验证。

 

下面看看Duwamish 7.0中是如何实现Salt运算:

1BusinessFacade/CustomerSystem classCreate Customer()方法

public bool CreateCustomer(String emailAddress,

                           byte [] password,

                           String name,

                           String address,

                           String country,

                           String phoneNumber,

                           String fax,

                           out CustomerData custData)

{

    // create a salted password

    byte [] saltedPassword = CreateDbPassword(password);

 

    //

    // Create a new row

    //

    custData = new CustomerData();

   

    DataTable table = custData.Tables[CustomerData.CUSTOMERS_TABLE];

    DataRow row = table.NewRow();

    //

    // Fill input data into new row

    //

    row[CustomerData.EMAIL_FIELD] = emailAddress;

    row[CustomerData.PASSWORD_FIELD] = saltedPassword;

    row[CustomerData.NAME_FIELD] = name;

    row[CustomerData.ADDRESS_FIELD] = address;

    row[CustomerData.COUNTRY_FIELD] = country;

    row[CustomerData.PHONE_FIELD] = phoneNumber;

    row[CustomerData.FAX_FIELD] = fax;

    //

    // Add it to the table

    //

    table.Rows.Add(row);

    // 调用Business rules tierCustomer Class

    // Insert the customer using the business rules

    //

    return (new Customer()).Insert(custData);

}

首先调用Facade/CustomerSystem 类的私有方法CreateDbPassword(),获取对散列执行Salt运算结果(长度为24个字节的byte数组),然后调用Business rules tier中的Customer classInsert()方法,将用户信息,包括密码存放在数据库中。

 

2Facade/CustomerSystem 类的私有方法 CreateDbPassword()

// create salted password to save in Db

private byte [] CreateDbPassword(byte[] unsaltedPassword)

{

          //Create a salt value

          byte[] saltValue = new byte[saltLength];

          RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

          //用加密型强随机字节填充的数组

          rng.GetBytes(saltValue);

         

          return CreateSaltedPassword(saltValue, unsaltedPassword);

}

上述代码片断使用 .NET Framework RNGCryptoServiceProvider 创建一个随机的数字字符串。RNG 表示随机数生成器。该类可以创建一个任意长度的随机字节数组,长度由您指定。您可以使用此随机字节数组作为散列算法的Salt值。要采用这种方法,必须安全地存储该Salt值。

 

saltLength=4(常量),Duwamish 7 示例用RNGCryptoServiceProvider创建一个 4 字节 Salt 值。然后调用Facade/CustomerSystem 类的私有方法CreateSaltedPassword(),获取对散列执行Salt运算后的结果。

 

3Facade/CustomerSystem 类的私有方法CreateSaltedPassword()

// create a salted password given the salt value

private byte[] CreateSaltedPassword(byte[] saltValue, byte[] unsaltedPassword)

{

          // add the salt to the hash

          byte[] rawSalted  = new byte[unsaltedPassword.Length + saltValue.Length];

         

          // Copies all the elements of the current one-dimensional System.Array to the specified one-dimensional System.Array starting at the specified destination System.Array index.

         

          unsaltedPassword.CopyTo(rawSalted,0);

          saltValue.CopyTo(rawSalted,unsaltedPassword.Length);

         

          //Create the salted hash                      

          SHA1 sha1 = SHA1.Create();

          byte[] saltedPassword = sha1.ComputeHash(rawSalted);

 

          // add the salt value to the salted hash

          byte[] dbPassword  = new byte[saltedPassword.Length + saltValue.Length];

          saltedPassword.CopyTo(dbPassword,0);

          saltValue.CopyTo(dbPassword,saltedPassword.Length);

 

          return dbPassword;

}

 

该方法根据传入的Salt值(长度为4个字节的byte数组)和已执行散列运算的密码(长度为20个字节的byte数组),拼接为长度为24byte数组。然后对上述拼接后的数组再进行SHA1散列运算,得到结果saltedPassword(长度为20个字节的byte数组)。

 

最后将saltedPassword(长度为20个字节的byte数组)和Salt值(长度为4个字节的byte数组)拼接为dbPassword(长度为4个字节的byte数组)返回。

 

3,调用BusinessRules/Customer类的Insert()方法。

Insert()方法根据传入的CustomerData对象,验证数据的合法性,然后调用Data Access tierCustomers对象的InsertCustomer()方法。

具体代码请参考Duwamish 7.0范例。

 

4,调用DataAccess/Customers类的InsertCustomer()方法。

InsertCustomer()方法根据传入的CustomerData对象,调用Database端的Stored Procedure,执行真正的数据库insert操作。可以观察到Duwamish7 DatabaseCustomers表的Password字段类型为binary且长度为24

具体代码请参考Duwamish 7.0范例。

 

下一篇POSTDuwamish密码分析篇 Part 2将分析【用户登录】流程的密码验证过程。

 

本手册首先从系统门户安全角度入手,全面分析了系统可能遭受的非法入侵和破坏途径;特别针对读者在网页浏览时经常遇到的恶意代码问题,提供了有效的防卫方法。 另外,如果你在使用电脑的过程中遗忘了众多的密码而感到一愁莫展、无计可施时,翻开本手册你就会找到令人满意的解决方法;并且本手册还就大家在网上冲浪中经常遇到的诸如QQ密码被盗、加密网页、下载限制等普遍比较关心、棘手的问题给出了可行的解决办法。 本手册内容精彩纷呈,解密、加密之法所向披靡,实为你案头必备之物。 目录 第1章 Windows 系统登录口令破解与反破解 1.1 Windows 98系统登录口令破解与反破解 1.1.1 Windows 98系统“个性化”登录口令设置 1.1.2 Windows 98登录密码解除 1.1.3 Windows 98登录口令反破解 1.2 Windows 2000系统登录口令的破解与反破解 1.2.1 Windows 2000系统帐户登录口令设置 1.2.2 Windows 2000系统登录口令的解除 1.2.3 Windows 2000系统登录反破解 1.3 Windows XP登录口令破解与反破解 1.3.1 Windows XP帐户口令设置 1.3.2 Windows XP登录口令破解 1.3.3 Windows XP系统登录口令反破解 1.4 系统登录反破解安全策略 1.4.1 组策略之登录安全策略 1.4.2 注册表之登录安全策略 第2章 Windows操作系统密码的破解与反破解 2.1 文件夹密码的解除与保护 2.1.1 EFS加密文件的解除 2.1.2 共享密码的解除 2.1.3 文件属性加密解除 2.1.4 文件专业保护工具 2.2 系统之星密码的破解 2.2.1 系统之星密码的破解 2.2.2 系统之星密码保护策略 2.3 屏保密码的破解 2.4 电源管理密码的破解 2.5 分级审查密码解除 2.6 系统密码的保护与管理 2.6.1 危险的密码 2.6.2 密码安全测试器 2.6.3 密码管理工具 第3章 注册表的限制与解除 3.1 注册表项目的标用与解除 3.2 注册表项目的隐藏与解除 3.3 通过注册表自动运行的恶意程序解除 3.3.1 木马自动运行的方法 3.3.2 Windows程序自启动的方法 第4章 文件加密、解密不求人 4.1 办公文档加密与解密 4.1.1 Office文档加密与解密 4.1.2 WPS文档加密与解密 4.1.3 IBM、Lotus加密文档的解密 4.2 压缩文件的加密与解密 4.2.1 压缩文件加密 4.2.2 压缩文件解密 4.2.3 加密压缩文件的安全 4.3 SWF文件解密 4.4 解密PDF文档 4.5 电子书反编译 4.5.1 E书伴侣帮你进行资料恢复 4.5.2 Chm电子书反编译 4.6 图片的加密保护 第5章 揭开硬件的秘密 5.1 BIOS密码的解密 5.1.1 BIOS密码的设置 5.1.2 破解BIOS密码 5.2 还原卡密码的解密 5.3 DVD区码解密 5.4 CD防拷贝突破 5.5 加密光盘解密 5.5.1 加密光盘的解密 5.5.2 光盘加密保护——光盘加密大师 5.6 加密狗解密 5.7 电子识别墨水破解 第6章 网络冲浪突破 6.1 聊天工具密码攻防战 6.1.1 QQ密码攻防战 6.1.2 ICQ密码攻防 6.2 忘记邮箱密码怎么办 6.2.1 Foxmail邮箱加密与解密 6.2.2 Outlook邮箱加密与解密 6.3 你不能不知的下载突破 6.3.1 突破单线程下载限制 6.3.2 突破不能下载的SWF文件 6.3.3 突破图片下载 6.3.4 突破影音文件下载限制 6.4 冲破网页的限制 6.4.1 突破锁定右键 6.4.2 突破禁止复制/保存 6.4.3 网页信息解密 6.4.4 防止网页破解的方法 6.5 消灭讨厌的网络广告 6.5.1 MyIE2过滤弹出式窗口广告 6.5.2 广告杀手Ad Killer 6.5.3 Zero Popup使用就这么简单 6.5.4 MSN Toolbar助你上网一臂之力 6.6 突破网吧限制 6.6.1 手工突破网吧限制 6.6.2 突破网吧下载限制 6.6.3 注册表突破妙招 6.6.4 五招突破硬盘操作 6.6.5 突破桌面限制 6.6.6 删除你的上网信息 6.6.7 网吧防破解方法 6.6.8 恶意代码防御战 6.6.9 用好恶意代码防治软件
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值