数据库里账号的密码,需要怎样安全的存放?—— 密码哈希(Password Hash)
最早在大学的时候,只知道用 MD5 来存用户的账号的密码,但其实这非常不安全,而所用到的哈希函数,深入挖掘,也发现并不简单……
一、普通的 Hash 函数
哈希(散列)函数是什么就不赘述了。
1、不推荐
RC4, MD4, MD5, SHA-0, SHA-1, DES, 2DES 等
2、推荐
SHA-2(SHA-256, SHA-384, SHA-512)、SHA-3、Blake2 等
美国国家标准和技术协会(NIST)宣布,2010 年后开始逐步取消 SHA-1 作为安全哈希算法的资格,取而代之的是其更强大的变异算法:SHA-224、SHA-256、SHA-384 和 SHA-512。无论是否遵循 NIST 的标准,至少使用 SHA-256 算法加密密码总是好的。
二、应对普通哈希容易被破解的策略
就像攻与矛的互相增强,哈希函数哪怕用到 SHA-3 以上,都还是有被轻易破解的风险。于是我们有其他额外的办法来解决这个问题。
1、加盐(salt)
加盐就是对目标字段哈希前,拼接上另一个字段(salt)。
注:盐值加到字段之前较为普遍。
加盐对防彩虹表很有效。
注意点:
- 盐不能太短
- 盐不能重复使用(否则一破解,所有的都遭殃)
- 盐随机变化(例如,虽用户名不重复,但用户名不能拿来当盐)
盐的本质是将
无差别攻击转化为针对性攻击。
1.1、【拓展】针对 salt 的另一种做法 —— HMAC
HMAC(Keyed-Hashing for Message Authentication)其实也是一种特殊的加盐,只是这个 salt 用更安全的密钥代替了。
具体介绍可以看我之前一篇:《破解另一家网站的反爬机制 & HMAC 算法》
2、慢哈希
高端的显卡(GPU)和定制的硬件可以每秒进行数十亿次哈希计算,因此这类攻击依然可以很高效。为了降低攻击者的效率,我们可以使用慢哈希,即迭代进行很多次哈希运算。
那么迭代多少次比较安全呢?来自 NIST 官方的建议:
- 2000 年 9 月,建议迭代一千次
- 2015 年 - 2018年,建议迭代一万次
- 2017 年 6 月,建议迭代十万次
三、密码哈希函数(Password Hash)
密码哈希函数(Password Hash)可以用来应对普通哈希容易被破解的问题(也用到了上面所提到的两个策略)。
下面列举的顺序是按照时间顺序,安全程度和推荐指数也逐级递增。
1、PBKDF2
比较老,很少有人用了,略。
2、Bcrypt
这是我司目前用的。(不过有过时的隐患,建议换掉)
(1)介绍
bcrypt 是由 Niels Provos 和 DavidMazières 基于 Blowfish 密码设计的密码哈希函数,于 1999 年在 USENIX 上提出。
bcrypt 函数是 OpenBSD 和其他系统(包括某些 Linux 发行版,例如 SUSE Linux)的默认密码哈希算法。
(2)使用(Node.js)
安装:npm i bcryptjs
bcryptjs跟 C++ 的 bcrypt 兼容,但因为是纯 JavaScript 编写的,因此速度较慢(约 30%)。
用法:
Sync 方法(Async 方法略):
const bcryptjs = require('bcryptjs');
// 1、生成 安全因子
const salt = bcrypt.genSaltSync(10);
// 2、执行 哈希函数
const password = bcryptjs.hashSync(plainPassword, bcryptjs.genSaltSync(salt));
// 另一种方法:快速执行
const SALT_FACTOR = 10;
const password = bcryptjs.hashSync(plainPassword, bcryptjs.genSaltSync(SALT_FACTOR));
// 3、比较是否相等
bcryptjs.compareSync(plainPassword, password);
注:代码里出现的安全因子,值的大小决定了哈希函数会有多慢。(即慢哈希)
3、Scrypt
没用过,略。
4、Argon2
(1)介绍
2013 年 NIST(美国国家标准与技术研究院)邀请了一些密码学家一起,举办了密码哈希竞赛 PHC(Password Hashing Competition)。Argon2 在 2015 年 7 月赢得了冠军。
大赛列出了参赛算法可能面临的攻击手段:
- 哈希算法破解(原值还原、哈希碰撞等);
- 查询表/彩虹表攻击;
- CPU 优化攻击;
- GPU、FPGA、ASIC 等专用硬件攻击;
- 旁路攻击;
(2)使用(Node.js)
1、准备
You can skip this section if the prebuilt binaries work for you.
You MUST have a node-gyp global install before proceeding with install, along with GCC >= 5 / Clang >= 3.3. On Windows, you must compile under Visual Studio 2015 or newer.
node-argon2 works only and is tested against Node >=10.0.0.
---
OSX
To install GCC >= 5 on OSX, use homebrew:
$ brew install gcc
Once you've got GCC installed and ready to run, you then need to install node-gyp, you must do this globally:
$ npm install -g node-gyp
Finally, once node-gyp is installed and ready to go, you can install this library, specifying the GCC or Clang binary to use:
$ CXX=g++-6 npm install argon2
NOTE: If your GCC or Clang binary is named something different than g++-6, you'll need to specify that in the command.
2、安装
npm i argon2
3、使用
const argon2 = require('argon2');
(async () => {
try {
// const hash = await argon2.hash("password");
// 更多选项(以下都是默认值)
const hash = await argon2.hash("password", {
type: argon2.argon2i,
hashLength: 32, // 哈希函数输出的字节长度(请注意,生成的哈希是使用Base64编码的,因此长度将增加约1/3)
timeCost : 3, // 时间成本是哈希函数使用的通过次数(迭代次数)
memoryCost: 2 ** 16, // 默认 4096(单位 KB,即 4MB)
parallelism :1, //用于计算哈希值的线程数量。每个线程都有一个具有memoryCost大小的内存池
})
console.log("hash", hash)
const is = await argon2.verify(hash, "password")
console.log("is", is) // true
} catch (err) {
console.error("err", err)
}
})()
(3)参数
1、type:
argon2d更快且对GPU攻击具有高度抵抗力,这对于加密货币很有用argon2i速度较慢且可以抵御权衡攻击,因此首选用于密码哈希和密钥派生argon2id是上述内容的混合组合,可以抵抗GPU和权衡攻击
因为我们是用于密码的 hash,用默认的 argon2i 即可。
2、(慢)哈希相关参数
① memoryCost 内存开销,它定义了内存的使用情况
好的起点是 0.75 *(RAM / number_of_users) 起步。
② parallelism 并行程度,它定义了线程的数量
最佳起点是内核数。
③ timeCost 时间开销,它定义了执行的时间
建议在系统上运行它,并确定与内存和处理器使用时间限制相匹配的最大参数。
如前所述,本质是在安全性和可用性之间取得平衡。
3、其他参数
salt:默认值是未设置,将生成加密安全的随机盐。
saltLength:默认16。
version:您不应更改此设置,因为最新版本更强大。
5、密码哈希是如何解决普通哈希容易被破解的问题
上面介绍了 二、应对普通哈希容易被破解的策略 ,我们可以看看密码哈希是如何运用并符合这些策略的。
(1)针对 salt
密码哈希使用 CSPRNG(Cryptographically Secure Pseudo-Random Number Generator)密码学安全伪随机数生成器生成盐。
CSPRNG 是加密安全(Cryptographically Secure)的,(加密安全的意思即)意味着用它产生的随机数更加随机,且不可预测。
普通的计算机随机数算法并不是很随机。
注:盐值本身就在存在于哈希后的字符串中(其实还可能包括版本、慢哈希迭代次数等),当调用跟明文比对的方法时,模块内部会提取出盐值进行验证。
(2)针对 慢哈希
Bcryoy 的安全因子和 Argon2的 timeCost 参数,都是针对慢哈希的配置。
6、结论
我司使用的 Bcrypt 其实在今年(2020年),已经不安全了,推荐至少使用 Scrypt,有条件上 Argon2。
四、常见问题
问1:我用我自己实现哈希算法,不用公开现成的,越古怪越好,坏人不就猜不到了吗?
答:不建议。
首先介绍下密码学上的柯克霍夫原则(Kerckhoffs's principle,也称为柯克霍夫假说、公理、或定律),由奥古斯特·柯克霍夫在 19 世纪提出:即使密码系统的任何细节已为人悉知,只要密匙(key,又称密钥或秘钥)未泄漏,它也应是安全的。信息论的发明者克劳德·香农则改成说:“敌人了解系统”,这样的说法则称为香农箴言。
基于这个原则:
- 1、你自己实现的再古怪,毕竟你不是密码专家,很难确保不被坏人破解(可能自己实现后看似复杂,实际更容易破解了)。
- 2、如果自己包含哈希算法的代码泄露,它很脆弱,难保不会被坏人破解。
问2:既然现在都是 https,前端传给后端的明文密码,就懒得加哈希了,可以吗?
还是建议前端也进行哈希(虽然前端的哈希算法容易暴露)。不要漏掉任何一个环节。
五、实操
Dropbox 公司曾公开分享过自己对用户账号的密码加密的策略,使用了三层加密:

1、password
即明文密码。
2、SHA512
在 bcrypt 前做 SHA512,是因为有些 bcrypt 实现会把散列值长度截至 72 字节,从而降低了密码的熵值,而有的则允许变长密码,这样容易受到 DoS 攻击。使用 SHA512 散列可以得到固定长度的 512 字节散列值,避免了上述的两个问题。
”而有的则允许变长密码,这样容易受到 DoS 攻击“,这句话我不是很理解,待写。
3、bcrypt
上面说过,不赘述了。
4、AES256
AES256 会用到密钥,俗称胡椒粉(pepper)。密钥需要被单独存储,最好存储在外部系统:如物理上隔离的服务端、甚至特殊的硬件设备(如 YubiHSM) 。
这里的 AES256 也可以用 HMAC 代替。不过前者安全性更好些。
数据库里账号的密码,需要怎样安全的存放?—— 密码哈希(Password Hash)的更多相关文章
- Sqlserver 中添加数据库登陆账号并授予数据库所有者权限
Sqlserver 中添加数据库登陆账号并授予数据库所有者权限 USE master GO --通过sp_addlogin创建登录名 --DEMO:登陆账号 --123456:登陆密码 ' --切换数 ...
- php 当前时间 当前时间戳和数据库里取出的时间datetime格式进行比较大小
php 当前时间 当前时间戳和数据库里取出的时间datetime格式进行比较大小 UNIX时间戳转换为日期用函数: date() ,date('Y-m-d H:i:s', 1500219870); 日 ...
- sql注入问题 java中将MySQL的数据库验证秘密加上 ' or '1'= '1 就可以出现万能密码
password的字符串中,加上 ' or '1'= '1 就可以制作出万能密码. 原因如下: 原代码中密码是123456 执行数据库查询语句 实际上执行的SQL语句是: select * from ...
- 云数据库 RDS 版怎么创建数据库和账号MySQL 5.7版
若要使用云数据库RDS,您需要在实例中创建数据库和账号.对于MySQL 5.7版本的实例,您需要通过RDS控制台创建一个初始账号,然后可以通过数据管理(DMS)控制台创建和管理数据库.本文将主要介绍在 ...
- Django数据操作F和Q、model多对多操作、Django中间件、信号、读数据库里的数据实现分页
models.tb.objects.all().using('default'),根据using来指定在哪个库里查询,default是settings中配置的数据库的连接名称. 外话:django中引 ...
- 解决secureCRT数据库里没有找到防火墙 '无'问题
中文版的secureCRT由于汉化的问题(把null翻译成无了),导致每次打开都会有个防火墙的错误提示:数据库里没有找到防火墙 '无' 此会话将尝试不通过防火墙进行连接.出现这个错误的原因是在secu ...
- TreeView树形控件递归绑定数据库里的数据
TreeView树形控件递归绑定数据库里的数据. 第一种:性能不好 第一步:数据库中查出来的表,字段名分别为UNAME(显示名称),DID(关联数据),UTYPE(类型) 第二步:前台代码 <% ...
- SQL删除数据库里所有表的外键,同时删除所有用户表
SQL删除数据库里所有表的外键,同时删除所有用户表 删除所有的用户表的外键,直接将下面的代码拷贝到数据库里执行即可: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...
- 在Code first中使用数据库里的视图
如果想在Code first中使用数据库里的视图 (不管你出于什么原因),目前的方法有2种. 一.使用Database.SqlQuery<T>("查询语句"),如: v ...
随机推荐
- 详解聚类算法Kmeans的两大优化——mini-batch和Kmeans++
本文始发于个人公众号:TechFlow,原创不易,求个关注 今天是机器学习专题的第13篇文章,我们来看下Kmeans算法的优化. 在上一篇文章当中我们一起学习了Kmeans这个聚类算法,在算法的最后我 ...
- 华为云+NextCloud(私人云盘搭建)
这几天发现了牛客+华为云的返现活动,免费用一年,赶紧的去搞了一个折腾折腾.(相关软件下载链接在最下面) 噔噔噔!!! 102822985.png) 废话少说,开始搭建. 基础环境部署 Apache安装 ...
- Qt 事件和信号的关系
Qt的事件是windows的底层消息封装而成的.这个消息和MFC里的消息是同一概念,都是指键盘.鼠标等的按压.松开等消息.例如按下键盘后,windows系统会发出一个 WM_KEYDOWN的消息,Qt ...
- vim-0-indent(缩进)
缩进: 参考自http://liuzhijun.iteye.com/blog/1831548,http://blog.csdn.net/chenxiang6891/article/details/41 ...
- PHP7内核(七):常见变量类型的基本结构
上篇文章讲述了变量的存储结构zval,今天我们就来学习一下几个常见变量类型的基本结构. 一.类型一览 zval中的u1.v.type用来存储变量的类型,而zval.value存储的是不同类型对应的值, ...
- Linux 常用命令速记
1.touch:创建文件,比如: touch a.txt 2.vi:编辑修改文件,比如: vi a.txt 打开编辑页面后: a 键:开始输入编辑: Esc键:结束输入内容 :wq:保存文件编辑内 ...
- 解决pycharm不能导入bs4模块问题
问题描述: 在导入bs4模块时有报错提示 “ Traceback (most recent call last): File "E:/project/code/py-pengfu/py-pf ...
- 怎么获取WebAPI项目中图片在服务端的路径
1.这是我的项目结构. 2.路径格式为:[http://服务器域名/文件夹/文件.扩展名] 测试:假如我要获取到[logo_icon.jpg]这张图.在浏览器的地址栏中输入上面那个格式的路径. 3.可 ...
- LayUI制作日历工作记录簿
标题不知道该如何取,大概就是用Lay UI的Table,制作一个日历,在日历上可以添加每天的工作简记录.记录下LayUI Table的一些用法,一些值得探索的地方在于日历生成后,给周末加背景色,当天加 ...
- twoway 系列命令绘制地图
twoway 系列命令绘制地图 ❝ 在谈到用stata绘制地图时,很多人首先想到的是spmap命令.其实,最常见的twoway系列命令就可以完成我们的大多数绘图需求. 对于spmap命令的使用,在线资 ...