建议114:MD5不再安全

MD5不再安全不是就算法本身而言的。如果从可逆性的角度出发,MD5值不存在被破解的可能性。

MD5被广泛应用于密码验证和消息完整性验证。假设新注册一个用户,当注册用户的密码第一次被存储到数据库时,往往会将其转换为MD5值存储:

        static string GetMd5Hash(string input)
{
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
return BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(input))).Replace("-", "");
}
} static void Main(string[] args)
{
string source = "liming's key";
string hash = GetMd5Hash(source);
Console.WriteLine("保存密码原文:{0}的MD5值:{1}到数据库。",source,hash); Console.Read();
}

输出为:

保存密码原文:liming's key的MD5值:B222558FD330454B08878C61FD595121到数据库。

如果MD5值存储在数据库中,当用户登录时,只需要验证MD5就可以检查用户输入的密码是否正确。如下:

        static void Main(string[] args)
{
Console.WriteLine("请输入密码,按回车键结束……");
string source = Console.ReadLine();
if (VerifyMd5Hash(source, "B222558FD330454B08878C61FD595121"))
{
Console.WriteLine("密码正确,准许登录系统。");
}
else
{
Console.WriteLine("密码有误,拒绝登录。");
}
} static bool VerifyMd5Hash(string input, string hash)
{
string hashOfInput = GetMd5Hash(input);
StringComparer comparer = StringComparer.OrdinalIgnoreCase;
return comparer.Compare(hashOfInput, hash) == ? true : false;
}

输出为:

请输入密码,按回车键结束……
liming's key
密码正确,准许登录系统。

处于隐私保护的目的,所以不直接存储密码。即便是一个银行系统,我们也不想让银行的后台管理人员看到我们的密码。而通过MD5值来校验,就可以确保无人可以查看或破解我们的密码,也达到了密码验证的目的。虽然有人可能会质疑,MD5的算法不是多对一的吗?也就是说,可能存在一个另外的密码,求出来的MD5值和我这个密码是一样的啊。但是,在实际应用场合中,这个概率会很小,小到可以忽略不计。

既然到目前为止所说的都是MD5的优点,那么,为什么说MD5是不安全的呢?因为,这个世界上还有一种方法叫做穷举法。由于用户安全意识先对薄弱,所以他们设置的密码很有可能是简单的数字集合。这种情况下如果破解密码。穷举法会派上很大的用处。以密码“8888”为例,测试下我们用穷举法破解所需时间:

        static void Main(string[] args)
{ Console.WriteLine("开始穷举法破解用户密码……");
string key = string.Empty;
Stopwatch watch = new Stopwatch();
watch.Start();
for (int i = ; i < ; i++)
{
if (VerifyMd5Hash(i.ToString(), "CF79AE6ADDBA60AD018347359BD144D2"))
{
key = i.ToString();
break;
}
}
watch.Stop();
Console.WriteLine("密码已破解,为:{0},耗时{1}毫秒。", key, watch.ElapsedMilliseconds);
}

输出为:

开始穷举法破解用户密码……
密码已破解,为:8888,耗时124毫秒。

可见,如果我们的密码过于简单,计算机甚至都不需要1秒的时间就能完成暴力破解。当然,这种算法不是针对MD5的可逆破解,而是非常愚蠢的穷举。现在,已经有很多免费的商业的MD5字典库,存储了相当数量字符串的MD5值,我们只要提交一个MD5值进去,立刻就可以得到他的原文,只要这个原文不是非常复杂。所以,从这方面来说,MD5不再安全。

因此,我们需要找一个办法来改进MD5求值了。目前,最通用的算法是多次使用MD5值发。我们修改一下GetMd5Hash方法,代码如下:

        static string GetMd5Hash(string input)
{
string hashKey = "Aa1@#$,.Klj+{>.45oP";
using (MD5CryptoServiceProvider md5 = new MD5CryptoServiceProvider())
{
string hashCode = BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(input))).Replace("-", "") + BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(hashKey))).Replace("-", "");
return BitConverter.ToString(md5.ComputeHash(UTF8Encoding.Default.GetBytes(hashCode))).Replace("-", "");
}
}

在改进后的方法中,我们首先设计了一个足够复杂的密码hashKey,然后将它的MD5值和用户输入密码的MD5值相加,再求一次MD5值作为返回值。经过这个过程以后,密码的长度就够了,复杂度也够了,要想通过穷举法来得到真正的密码成本也就大大增加了。

转自:《编写高质量代码改善C#程序的157个建议》陆敏技

编写高质量代码改善C#程序的157个建议——建议114:MD5不再安全的更多相关文章

  1. 编写高质量代码改善C#程序的157个建议[1-3]

    原文:编写高质量代码改善C#程序的157个建议[1-3] 前言 本文主要来学习记录前三个建议. 建议1.正确操作字符串 建议2.使用默认转型方法 建议3.区别对待强制转换与as和is 其中有很多需要理 ...

  2. 读书--编写高质量代码 改善C#程序的157个建议

    最近读了陆敏技写的一本书<<编写高质量代码  改善C#程序的157个建议>>书写的很好.我还看了他的博客http://www.cnblogs.com/luminji . 前面部 ...

  3. 编写高质量代码改善C#程序的157个建议——建议157:从写第一个界面开始,就进行自动化测试

    建议157:从写第一个界面开始,就进行自动化测试 如果说单元测试是白盒测试,那么自动化测试就是黑盒测试.黑盒测试要求捕捉界面上的控件句柄,并对其进行编码,以达到模拟人工操作的目的.具体的自动化测试请学 ...

  4. 编写高质量代码改善C#程序的157个建议——建议156:利用特性为应用程序提供多个版本

    建议156:利用特性为应用程序提供多个版本 基于如下理由,需要为应用程序提供多个版本: 应用程序有体验版和完整功能版. 应用程序在迭代过程中需要屏蔽一些不成熟的功能. 假设我们的应用程序共有两类功能: ...

  5. 编写高质量代码改善C#程序的157个建议——建议155:随生产代码一起提交单元测试代码

    建议155:随生产代码一起提交单元测试代码 首先提出一个问题:我们害怕修改代码吗?是否曾经无数次面对乱糟糟的代码,下决心进行重构,然后在一个月后的某个周一,却收到来自测试版的报告:新的版本,没有之前的 ...

  6. 编写高质量代码改善C#程序的157个建议——建议154:不要过度设计,在敏捷中体会重构的乐趣

    建议154:不要过度设计,在敏捷中体会重构的乐趣 有时候,我们不得不随时更改软件的设计: 如果项目是针对某个大型机构的,不同级别的软件使用者,会提出不同的需求,或者随着关键岗位人员的更替,需求也会随个 ...

  7. 编写高质量代码改善C#程序的157个建议——建议153:若抛出异常,则必须要注释

    建议153:若抛出异常,则必须要注释 有一种必须加注释的场景,即使异常.如果API抛出异常,则必须给出注释.调用者必须通过注释才能知道如何处理那些专有的异常.通常,即便良好的命名也不可能告诉我们方法会 ...

  8. 编写高质量代码改善C#程序的157个建议——建议152:最少,甚至是不要注释

    建议152:最少,甚至是不要注释 以往,我们在代码中不写上几行注释,就会被认为是钟不负责任的态度.现在,这种观点正在改变.试想,如果我们所有的命名全部采用有意义的单词或词组,注释还有多少存在的价值. ...

  9. 编写高质量代码改善C#程序的157个建议——建议151:使用事件访问器替换公开的事件成员变量

    建议151:使用事件访问器替换公开的事件成员变量 事件访问器包含两部分内容:添加访问器和删除访问器.如果涉及公开的事件字段,应该始终使用事件访问器.代码如下所示: class SampleClass ...

  10. 编写高质量代码改善C#程序的157个建议——建议150:使用匿名方法、Lambda表达式代替方法

    建议150:使用匿名方法.Lambda表达式代替方法 方法体如果过小(如小于3行),专门为此定义一个方法就会显得过于繁琐.比如: static void SampeMethod() { List< ...

随机推荐

  1. Oracle ASM操作管理

    查看ASM磁盘情况 SQL> select group_number,disk_number,mount_status,header_status,mode_status,state,failg ...

  2. 浅谈OPP

    了解Java或C#等面向对象编程语言的的程序员比较熟悉类和对象以及OOP. 一谈起OOP,就会想起教科书式的OOP概念:封装.继承.多态.粗浅的解释封装就是对数据进行隐藏:继承就是子类继承父类(cla ...

  3. python _/__/__**__区别

    "_"单下划线:"单下划线 " 开始的成员变量叫做保护变量,意思是只有类对象(即类实例)和子类对象自己能访问到这些变量,需通过类提供的接口进行访问:不能用'fr ...

  4. 华为OJ机试题目:两个大整数相乘(纯C语言实现两个大整数相乘,两种方法实现大数相乘)

    题目描述: 输出两个不超过100位的大整数的乘积. 输入: 输入两个大整数,如1234567 123 输出: 输出乘积,如:151851741 样例输入: 1234567 123 样例输出: 1518 ...

  5. Android 4 学习(18):搜索

    参考<Professional Android 4 Development> 搜索 通过下面这几种方式可以给应用程序添加搜索功能: Search Bar Search View Quick ...

  6. GPRS 通信

    最近使用SIM900A GSM/GPRS模块,做一些简单物联网信息投递. 1.选取何种流量卡 一般来讲,需要带身份证去运营商办理新的电话卡.对于小流量用户,实际上就是办一个最低月租即可,因为套餐会赠送 ...

  7. PropertyGrid 重难点总结

    PropertyGrid的界面组成与不同部分的名称如下图所示. 本博文不算是自己写作的,只是将PropertyGrid中的几项十分有用的功能的应用方面的文字进行一下总结,希望以后大家对Property ...

  8. C# 在根据窗体中的表格数据生成word文档时出错

    出错内容为:

  9. SpringBoot入门之spring-boot-maven-plugin

    spring-boot-maven-plugin插件是将springboot的应用程序打包成fat jar的插件.首先我们说一下啥叫fat jar.fat jar 我们暂且叫他胖jar吧,实在是找不到 ...

  10. JS倒计时,自动提交表单!

    <form id="frm" action="http://www.baidu.com"> 考试还剩余<div id="time&q ...