MD5 or Bcrypt?

摘要

首先是一个错误的认识观念问题,很多人觉得MD5是一个加密算法。不然,他实则是一种摘要算法,也可以叫哈希函数。他的作用是将目标文本转换成具有相同长度、不可逆的杂凑字符串。而加密算法和他恰恰相反,是将目标转换成具有不同长度、可逆的密文。

MD5简介

一般来说由于摘要算法他的单向运算,具有一定的不可逆性也就是说信息无法被还原了,所以成为加密算法中的一个构成部分。MD5他的具体原理可以看B站这个up的容易理解 传送门

但在现在看来任何场景下都最好不要使用MD5,因为他不够安全,跟存储明文没啥两样。可以使用诸如SHA256、SHA512这类其他的摘要算法来替代。MD5的不够安全从下面说起:

MD5的不安全性

因为这种哈希算法很固定,同一个传入的字符串得到的hash字符串是相同的。以现在的手段来说,可以采用:

  1. 暴力枚举直接枚举原文并计算哈希值,然后再去比对一致的信息摘要
  2. 字典爆破,用一个巨大的字典来存储原始信息和对应哈希值的键值对,通过碰撞来比对映射关系
  3. 彩虹表,也是现在最多使用的办法(像之前的*习通信息泄露事件中)

虽然说这些破解方法的成本比较高,但在如今计算机算力势不可挡的发展下,利用分布式计算还是能很有效的进行破解。

加盐MD5

那可能会有人说了,加盐了解一下?确实加盐主要就是为了增加嗨客的计算成本,因为有了盐值要重新计算彩虹表所以在抵御彩虹表上确实有作用。但真的就安全了吗?这种情况下虽然每个加密hash字符串使用了随机的盐进行hash,但破解的方向转变成了哈希碰撞的概率。比如就算我不知道你的密码,不知道你的盐值。只要我能找到一个值经过加盐的hash后与你加盐后的密码一致照样能登录你的账号。

也就是说这还是个算力问题。除了知名的hashcat,下面是在一个靶场环境下使用 John (john the ripper)来爆破MD5

总的来说,用MD5、MD5加盐来存密码都是不够安全的。那更换其他的加密算法呢?这需要考虑一个大问题,那就是怎么去存储加密用的密钥呢?当数据泄露时又怎么保证密钥就能独善其身呢?

Bcrypt

更加安全的解决方案是现在广泛使用的bcrypt,他是基于 eksblowfish 算法设计的加密哈希函数。他的主要特点如下:

  1. 产生随机盐:这样对于同样的原始数据得到的hash都不同。他先生成随机的盐,再拿盐和原始数据进行hash
  2. 自适应性:他可以动态调整工作因子,前面提到了算力问题,而因为他这个特性任你算力多强他会自己增加迭代次数将加密速度控制在一个范围里面,直接硬抗暴力搜索。

那盐是随机的,每次做出的菜味道都不一样,又该怎么比对内容的一致性呢?他的校验是先从hash中取出salt,再去与内容进行hash,然后去比对之前加密的hash。比如下面加密的字符串:

$2a$10$te3Yi7z4fiH2xRw6vReet.XHRbd1iz711ykk6bFAL4y/nk213QOXS

$2a$为hash算法的唯一标识,这里表示Bcrypt。10是工作因子,代表2的10次方。te3Yi7z4fiH2xRw6vReet.这部分22长的字符是16字节的salt经base64编码后得到的,最后的部分是计算后的 Base64 哈希值(24 字节)

看一个例子:

package main

import (
"golang.org/x/crypto/bcrypt"
"fmt"
) func encryptByBcrypt(str string) string {
bytes := []byte(str)
hashedBytes, err := bcrypt.GenerateFromPassword(bytes, bcrypt.DefaultCost)
if err != nil {
fmt.Println(err)
return ""
}
return string(hashedBytes)
} func compare(format, input string) bool {
err := bcrypt.CompareHashAndPassword([]byte(format), []byte(input))
if err != nil {
return false
}
return true
} func main() {
var (
// 用户输入
input1 = "admin666"
input2 = "admin"
// 数据库中存储的
dbData = encryptByBcrypt("admin666")
) fmt.Printf("用户输入%s的加密结果: %s\n", input1, encryptByBcrypt(input1))
fmt.Printf("用户输入%s的加密结果: %s\n", input2, encryptByBcrypt(input2))
fmt.Printf("数据库中的加密字符串: %s\n", dbData)
for i := 2; i < 5; i++ {
fmt.Printf("第%d次登录时对%s加密的结果: %s ", i, input1, ecryptByBcrypt(input1))
fmt.Printf("%s是否匹配数据库中的值:%v\n", input1, compare(dbData, input1))
} fmt.Printf("%s 是否匹配数据库中的值:%v", input2, compare(dbData, input2))
} //结果: //admin666的加密结果: $2a$10$Q1VWBHXkPxTT0oLQtvaKSOfXmz2CJIN2z7y1A9L.rcciJyxhtlMcu
//admin的加密结果: $2a$10$Z9JHgQQSEcQGIuVNhFmbpe3IdWLgR2ZjFhKQHU.2XXmWvrdXHpfZm
//数据库中的加密字符串: $2a$10$/IdAYwPf/mpnxH083nUxMuqigOuDQszgoVTjFnYMJJe3B0DFP3qNG
//第2次登录时对admin666加密的结果: $2a$10$TvHq/QzyK2mtW/VmDKEcPOp2KJ4qiKMcKzC709H0aUfqqv8dgC66u admin666是否匹配数据库中的值:true
//第3次登录时对admin666加密的结果: $2a$10$6X7DgbitO97hjDZfVfQa5.h1JNXWg0k65hc0PjhXHUnKhZhNFvoeK admin666是否匹配数据库中的值:true
//第4次登录时对admin666加密的结果: $2a$10$qtbVqqzU6V07RwMDg5.eG.SHzekcozQPeKqTewfXL9gVgt91OLXbe admin666是否匹配数据库中的值:true
//admin 是否匹配数据库中的值:false

可以看到当输入和数据库中存储加密的原始值相同时(admin666),虽然每次加密admin666的结果都不一样,但都能匹配通过,但输入不一样时(admin)一定不匹配。

再举一个看看他如何抵御暴力搜索:

package main

import (
"fmt"
"golang.org/x/crypto/bcrypt"
"time"
) func main() {
for cost := 10; cost <= 20; cost++ {
start := time.Now()
_, err := bcrypt.GenerateFromPassword([]byte("password"), cost)
if err != nil {
return
}
end := time.Since(start)
fmt.Printf("工作因子: %d, 耗时: %v\n", cost, end)
}
} //结果
//工作因子: 10, 耗时: 58.073ms
//工作因子: 11, 耗时: 119.7431ms
//工作因子: 12, 耗时: 229.9239ms
//工作因子: 13, 耗时: 473.358ms
//工作因子: 14, 耗时: 934.7008ms
//工作因子: 15, 耗时: 1.8949588s
//工作因子: 16, 耗时: 3.7853585s
//工作因子: 17, 耗时: 7.808824s
//工作因子: 18, 耗时: 23.0166827s
//工作因子: 19, 耗时: 36.2171079s
//工作因子: 20, 耗时: 1m1.0674139s

可以看到每调整了cost(工作因子),加密的时间都会不断增加。当嗨客来硬碰硬的时候就直接提高cost增加他的成本。

MD5 or Bcrypt?的更多相关文章

  1. MD5,SHA1 都是哈希 摘要算法 MD5+SALT BCRYPT

    为什么说 MD5 是不可逆的? md5是摘要算法,你学数据结构的时候学过哈希表吧?也有叫散列的.md5就是干那个用的 当然,如果硬要说的话...你可以用md5 hash一段字符串,也算是加密好了,不过 ...

  2. Java通过BCrypt加密

    一.概述 在用户模块,对于用户密码的保护,通常都会进行加密.我们通常对密码进行加密,然后存放在数据库中,在用户进行登录的时候,将其输入的密码进行加密然后与数据库中存放的密文进行比较,以验证用户密码是否 ...

  3. Web项目开发中常见安全问题防范

    本文章纯粹是个人收藏,其中各种也是略略了解,下面直接贴出知识点./捂脸/捂脸 计算机程序主要就是输入/输出,安全问题由此产生,凡是有输入的地方都可能带来安全风险.根据输入的数据类型,web应用主要有数 ...

  4. Ansible 小手册系列 十七(特性模块)

    异步操作和轮询 --- # Requires ansible 1.8+ - name: 'YUM - fire and forget task' yum: name=docker-io state=i ...

  5. 信安周报-第03周:DB系统表

    信安之路 第03周 前言 这周自主研究的任务如下: 任务附录的解释: 文件读写在通过数据库注入漏洞获取webshell的时候很有用 系统库和表存放了很多关键信息,在利用注入漏洞获取更多信息和权限的过程 ...

  6. php安全 过滤、验证、转义

    不要相信外部源 $_GET $_POST $_REQUEST $_COOKIE $argv php://stdin php://input file_get_contents() 远程数据库 远程ap ...

  7. mongodb导入数据,保创建新项目

    1.回顾 2.导入数据 2.1 excel数据表格 2.2 设计导入数据的路由 routes/users.js router.get('/upload', function (req, res, ne ...

  8. 如何生成安全的密码 Hash:MD5, SHA, PBKDF2, BCrypt 示例

    密码 Hash 值的产生是将用户所提供的密码通过使用一定的算法计算后得到的加密字符序列.在 Java 中提供很多被证明能有效保证密码安全的 Hash 算法实现,我将在这篇文章中讨论其中的部分算法. 需 ...

  9. 用户角色权限查询添加bug集锦 用户密码加密 MD5 加盐 随机盐 spring的加密bcrypt

    package cn.itcast.encode; import org.apache.commons.lang3.RandomStringUtils; import org.springframew ...

  10. 使用BCrypt算法加密存储登录密码用法及好处

    //导入import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; /** *使用BCrypt算法加密存储登录密码 ...

随机推荐

  1. SQL Server更改表字段顺序和表结构

    1.首先打开SqlServer,SSMS可视化工具.点击工具,再点选项. 2.在弹出的选项窗口中,点击Desinners,点击表设计和数据库设计器,将阻止保护勾去掉.点"确定" 3 ...

  2. JDK17和JDK8在windows上同时安装方便切换

    参考:https://blog.csdn.net/chencaw/article/details/121674479 一.JDK8的安装 1.电脑上已经安装了JDK8,安装主要步骤如下 (1)创建JA ...

  3. 🖖少年,该升级 Vue3 了!

    你好,我是 Kagol. 前言 根据 Vue 官网文档的说明,Vue2 的终止支持时间是 2023 年 12 月 31 日,这意味着从明年开始: Vue2 将不再更新和升级新版本,不再增加新特性,不再 ...

  4. 【Redis】SpringBoot集成Redis事务-亲测

    大家好,我是mep.今天一起来探讨一下Redis缓存的问题,SpringBoot如何集成Redis网上文章很多,基本都是介绍如何配置redisTemplate,如何调用,本文就不过多介绍了.这次我们研 ...

  5. Springboot实现注解判断权限

    Springboot实现注解判断权限 今天记录一下使用springboot的注解来给方法加权限 避免了每个方法都需要大量的权限判断 超级好用√ @ 目录 Springboot实现注解判断权限 1.创建 ...

  6. Java虚拟机(JVM):第六幕:自动内存管理 - 选择合适的垃圾收集器

    前言:在虚拟机的世界里面,内置了很多的垃圾收集器,但并不是说最先进的就是最好的.有一句话说的好"因地制宜": 一.Epsilon收集器 是一个无操作的收集器,但是贴切的来说是&qu ...

  7. Intervals 题解

    Intervals 题目大意 给定 \(m\) 条形如 \((l_i,r_i,a_i)\) 的规则,你需要求出一个长为 \(n\) 的分数最大的 01 串的分数,其中一个 01 串 \(A\) 的分数 ...

  8. Chromium Mojo通信

    Mojo 是一个跨平台 IPC 框架,它诞生于 chromium ,用来实现 chromium 进程内/进程间的通信.目前,它也被用于 ChromeOS. 在我们代码应用中,如何使用Mojo来作进程间 ...

  9. My Code Style

    大家都在写,跟风. 头文件 万能头.因为我刚学 OI 的时候怎么都背不住 algorithm 怎么拼( 变量 数组开全局,一些前后重名/只在某一部分用的变量开局部. 尽量不使用指针/ stl 迭代器等 ...

  10. 系统RAM几乎爆满与解决方法

    先说一遍,遇事不决就重启! 在电脑长时间运转下某些无良应用程序会产生大量的临时文件(目前我怀疑是有道云笔记) 最终导致系统爆炸 附图 在这种情况下,我下载了RAM实时监测我内存占用情况 结果发现pag ...