Hashed password with salted value——利用salt对密码进行散列

在没有学习web安全密码学部分的时候,我自己全部的密码都是同一个,而且是7位数字组成的。还好我本身没有什么财产好损失,不过都不会再犯这种愚蠢错误了。

     我们为什么需要对密码进行hash化,又为什么要在hash化之前给它加一个随机值?我想从一个例子分析起:我看过一个黑客的文章,讲的是他通过一些技术手段进入了一个刚刚起步的网站,他表示这个网站的安全问题非常严重,严重到可以拿到所有用户的用户名,邮箱和密码。大家可能都有这样的感受,就是不爱记忆太多的密码,所以大部分的网站或者应用都用了同一个密码!这样,黑客不仅可以进入邮箱,而且可以进入QQ、MSN等及时聊天系统,从而获得更多的私人关系和关心网信息,这也就是为什么会有那么多利用QQ进行诈骗亲朋好友的案例。还好写这个文章的黑客没有恶意,只是指出了这个网站的问题,否则后果不堪设想(由此看来大部分黑客的职业操守还是很高的o(∩_∩)o )。

     所以这个故事告诉我们,尽量不要用相同的密码,特别是重要的如银行账户之类的,一定要谨慎。那么大家现在都知道对密码进行hash,比如MD5之类的可以比较有效的防止被破译。但是事实真的是这样吗?

     如果直接对密码进行hash,那么黑客可以对一个已知密码进行散列,然后通过对比散列值得到某用户的密码。换句话说,虽然黑客不能取得某特定用户的密码,但他可以知道使用特定密码的用户有哪些!这时,我们可以通过加salt来从一定程度上解决这个问题。什么是salt,顾名思义salt这个“佐料”也就是在对密码进行hash时,添加的一个随机数。其基本想法是这样的——当用户首次提供密码时(通常是注册时),由系统自动往这个密码里撒一些“佐料”,然后再散列。而当用户登录时,系统为用户提供的代码撒上同样的“佐料”,然后散列,再比较散列值,以确定密码是否正确。因为salt值是系统随机生成的,即使两个用户用了相同的密码,他们的hash值也不一样,黑客找出密码的几率大大减小了(必须是密码和自己生成的散列值都跟用户相同,这个概率很低了)。

 

下面讨论一下用户注册的过程:

1)用户提供密码(以及其他用户信息);2)系统为用户生成Salt值;

3)系统将Salt值和用户密码连接到一起;4)对连接后的值进行散列,得到Hash值;

5)将Hash值和Salt值分别放到数据库中。

当用户登录时,相应的过程是:

1)用户提供用户名和密码;

2)系统通过用户名找到与之对应的Hash值和Salt值;

3)系统将Salt值和用户提供的密码连接到一起;

4)对连接后的值进行散列,得到Hash'(注意有个“撇”);

5)比较Hash和Hash'是否相等,相等则表示密码正确,否则表示密码错误。

 

感觉说的比较清楚了,也是结合了网上一些资料和自己的体会写出来的。下面用网上一篇博客里的代码详细的分析一下对密码执行散列和salt的方法。正好在学习c#也是对自己的一个提升。

原文链接http://www.cnblogs.com/esshs/archive/2005/03/29/127998.html

[csharp]  view plain copy
  1. <span style="font-size:16px;">using System;  
  2. using System.IO;  
  3. using System.Text;  
  4. using System.Security.Cryptography;  
  5.   
  6. namespace BookStore.Common  
  7. {  
  8.     /// <summary>  
  9.     /// Credentials 的摘要说明。  
  10.     /// 原理:  
  11.     /// 对密码执行散列运算  
  12.     /// 若要避免以明文形式存储密码,一种常见的安全做法是对密码执行散列运算。如以下代码所示,使用 System.Security.Cryptography 命名空间(它实现 160 位 SHA-1 标准)对密码进行散列运算。有关更多信息,请参见 SHA1 成员。  
  13.     /// 对散列执行 Salt 运算  
  14.     /// 虽然对密码执行散列运算的一个好的开端,但若要增加免受潜在攻击的安全性,则可以对密码散列执行 Salt 运算。Salt 就是在已执行散列运算的密码中插入的一个随机数字。这一策略有助于阻止潜在的攻击者利用预先计算的字典攻击。字典攻击是攻击者使用密钥的所有可能组合来破解密码的攻击。当您使用 Salt 值使散列运算进一步随机化后,攻击者将需要为每个 Salt 值创建一个字典,这将使攻击变得非常复杂且成本极高。  
  15.     /// Salt 值随散列存储在一起,并且未经过加密。所存储的 Salt 值可以在随后用于密码验证。  
  16.     /// </summary>  
  17.     public class Credentials  
  18.     {  
  19.         private static string key = "!48%0d-F=cj>,s&2";    //密钥(增加密码复杂度,好像比较多余)  
  20.         private const int saltLength = 4;                //定义salt值的长度  
  21.   
  22.         /// <summary>  
  23.         /// 对密码进行Hash 和 Salt  
  24.         /// </summary>  
  25.         /// <param name="Password">用户输入的密码</param>  
  26.         /// <returns></returns>  
  27.         public static byte[] HashAndSalt(string Password)  
  28.         {  
  29.             return CreateDbPassword(HashPassword(Password));  
  30.         }  
  31.   
  32.         /// <summary>  
  33.         /// 对用户输入的密码加上密钥key后进行SHA1散列  
  34.         /// </summary>  
  35.         /// <param name="Password">用户输入的密码</param>  
  36.         /// <returns>返回 160 位 SHA-1 散列后的的byte[](160位对应20个字节)</returns>  
  37.         private static byte[] HashPassword( string Password )  
  38.         {  
  39.             //创建SHA1的对象实例sha1  
  40.             SHA1 sha1 = SHA1.Create();  
  41.             //计算输入数据的哈希值  
  42.             return sha1.ComputeHash( Encoding.Unicode.GetBytes( Password + key ) );  
  43.         }  
  44.           
  45.         /// <summary>  
  46.         /// 比较数据库中的密码和所输入的密码是否相同  
  47.         /// </summary>  
  48.         /// <param name="storedPassword">数据库中的密码</param>  
  49.         /// <param name="Password">用户输入的密码</param>  
  50.         /// <returns>true:相等/false:不等</returns>  
  51.         public static bool ComparePasswords(byte[] storedPassword, string Password)  
  52.         {  
  53.             //首先将用户输入的密码进行Hash散列  
  54.             byte[] hashedPassword = HashPassword(Password);  
  55.   
  56.             if (storedPassword == null || hashedPassword == null || hashedPassword.Length != storedPassword.Length - saltLength)  
  57.             {  
  58.                 return false;  
  59.             }  
  60.   
  61.             //获取数据库中的密码的salt 值,数据库中的密码的后4个字节为salt 值  
  62.             byte[] saltValue = new byte[saltLength];  
  63.             int saltOffset = storedPassword.Length - saltLength;  
  64.             for (int i = 0; i < saltLength; i++){  
  65.                 saltValue[i] = storedPassword[saltOffset + i];  
  66.             }  
  67.               
  68.             //用户输入的密码用户输入的密码加上salt 值,进行salt  
  69.             byte[] saltedPassword = CreateSaltedPassword(saltValue, hashedPassword);  
  70.           
  71.             //比较数据库中的密码和经过salt的用户输入密码是否相等  
  72.             return CompareByteArray(storedPassword, saltedPassword);  
  73.         }  
  74.   
  75.         /// <summary>  
  76.         /// 比较两个ByteArray,看是否相等  
  77.         /// </summary>  
  78.         /// <param name="array1"></param>  
  79.         /// <param name="array2"></param>  
  80.         /// <returns>true:相等/false:不等</returns>  
  81.         private static bool CompareByteArray(byte[] array1, byte[] array2)  
  82.         {  
  83.             if (array1.Length != array2.Length)  
  84.             {  
  85.                 return false;  
  86.             }  
  87.             for (int i = 0; i < array1.Length; i++)  
  88.             {  
  89.                 if (array1[i] != array2[i])  
  90.                 {  
  91.                     return false;  
  92.                 }  
  93.             }  
  94.             return true;  
  95.         }  
  96.   
  97.         /// <summary>  
  98.         /// 对要存储的密码进行salt运算  
  99.         /// </summary>  
  100.         /// <param name="unsaltedPassword">没有进行过salt运算的hash散列密码</param>  
  101.         /// <returns>经过salt的密码(经过salt的密码长度为:20+4=24,存储密码的字段为Binary(24))</returns>  
  102.         private static byte[] CreateDbPassword(byte[] unsaltedPassword)  
  103.         {  
  104.             //获得 salt 值  
  105.             byte[] saltValue = new byte[saltLength];  
  106.             RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();  
  107.             rng.GetBytes(saltValue);  
  108.               
  109.             return CreateSaltedPassword(saltValue, unsaltedPassword);  
  110.         }  
  111.           
  112.         /// <summary>  
  113.         /// 创建一个经过salt的密码  
  114.         /// </summary>  
  115.         /// <param name="saltValue">salt 值</param>  
  116.         /// <param name="unsaltedPassword">没有进行过salt运算的hash散列密码</param>  
  117.         /// <returns>经过salt的密码</returns>  
  118.         private static byte[] CreateSaltedPassword(byte[] saltValue, byte[] unsaltedPassword)  
  119.         {  
  120.             //将salt值数组添加到hash散列数组后拼接成rawSalted数组中  
  121.             byte[] rawSalted  = new byte[unsaltedPassword.Length + saltValue.Length];   
  122.             unsaltedPassword.CopyTo(rawSalted,0);  
  123.             saltValue.CopyTo(rawSalted,unsaltedPassword.Length);  
  124.               
  125.             //将合并后的rawSalted数组再进行SHA1散列的到saltedPassword数组(长度为20字节)  
  126.             SHA1 sha1 = SHA1.Create();  
  127.             byte[] saltedPassword = sha1.ComputeHash(rawSalted);  
  128.   
  129.             //将salt值数组在添加到saltedPassword数组后拼接成dbPassword数组(长度为24字节)  
  130.             byte[] dbPassword  = new byte[saltedPassword.Length + saltValue.Length];  
  131.             saltedPassword.CopyTo(dbPassword,0);  
  132.             saltValue.CopyTo(dbPassword,saltedPassword.Length);  
  133.   
  134.             return dbPassword;  
  135.         }  
  136.   
  137.     }  
  138. }</span>  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 由于HashedPassword散列密码,是一个不可逆向的算法,因此无法直接解析或反向计算出原始密码。这是散列密码的基本特性,保证了密码的安全性。 如果需要验证一个密码是否与HashedPassword匹配,通常可以通过将用户输入的密码使用相同的加密算法进行哈希处理,并将结果与HashedPassword进行比较,如果匹配则代表输入的密码正确。 在实际应用中,为了增加密码强度,通常会采用带有“盐值”的哈希算法计算哈希值,这样即使原始密码相同,由于盐值的不同,计算出的HashedPassword也会不同,从而增加了黑客破解密码的难度。 ### 回答2: 要解析一个 byte HashedPassword 密码,需要使用相应的解析算法和密钥。以下是一个常见的解析过程: 1. 获取密钥:通常,解析一个加密的密码需要密钥。这个密钥可能是预先设定的,或者是通过其他方式生成的。确保你有正确的密钥。 2. 密钥衍生:如果需要,使用适当的密钥派生函数(如 PBKDF2 或 bcrypt)来从给定的密钥派生密钥材料。密钥衍生是为了增加密码的安全性。 3. 解密:使用所得到的密钥将 byte HashedPassword 进行解密。解密算法可能因具体情况而异,取决于加密密码时使用的算法。 4. 校验:在使用解密算法进行解密之后,你可能需要检查解密结果是否与期望的明文密码一致。对于一些现代的密码哈希算法,如 bcrypt 或 Argon2,不需要解密为明文密码,而是使用哈希比较函数直接与输入的明文密码进行比较。 需要注意的是,从 byte HashedPassword 解析出明文密码是一项敏感和复杂的任务。在实际应用中,最好借助密码学专家的指导和使用已被广泛接受的密码哈希和解密算法,以确保密码的安全性。同样,尽量避免储存明文密码,而是将其哈希和加盐后存储,这样即使密码泄露,攻击者也无法立即获取明文密码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值