密码哈希参考

简介: OWASP组织针对密码存储提供了一个专题指引,本文针对里面的密码存储方法进行了翻译,并对密码存储(or 密码哈希)算法基于Go语言的crypto库实现做了一下的性能测试,并收集了对应算法的实现标准链接,方便后续进一步学习。

背景

对于每一个提供用户账密认证方式的应用系统来说,密码存储都是一个非常至关重要的设计内容。系统实现过程的一个重要设计原则是,需要在攻击者对整个系统设计都了如指掌的前提下,系统的安全性仍然能够得到保证,即内部研发人员要无法作恶。而单单针对密码存储来讲,我们需要确保的是对应密码存储手段能够在攻击者拿到密码存储的内容时也无法逆向出来用户的密码。

OWASP组织针对密码存储提供了一个专题指引,本文针对里面的密码存储方法进行了翻译,并对密码存储(or 密码哈希)算法基于Go语言的crypto库实现做了一下的性能测试,并收集了对应算法的实现标准链接,方便后续进一步学习。

密码存储 “哈希” vs “加密”

哈希和加密都能安全保存敏感数据信息,但是在绝大部分的场景中,密码存储都是哈希,而不是加密。

其原因在于,哈希是一个“单向(One-way)”函数,几乎不能基于一个哈希后的取值逆向出来原文。即使攻击者拿到了密码哈希后的内容,攻击者也不能使用该内容用来恶意登录。

而加密是一个“双向(two-way)”函数,攻击者在理论上是可以通过加密内容获取到原文的。加密更适合存储用户的地址信息等个人敏感数据,这种信息的特点是需要展示原文在用户界面上。

针对密码存储增强的方法

Salting - 加盐

salt - “盐”,是一个唯一的,作为哈希过程的一部分添加到每个密码中。由于每个用户密码的盐是唯一的,攻击者必须使用各自的盐一次破解一个哈希,而不是一次计算一个哈希值并将其与每个密码存储的哈希进行比较。这使得破解大量哈希变得更加困难,因为所需的时间与哈希数量成正比。

加盐还可以防止攻击者使用彩虹表或基于数据库的查找来预先计算散列。最后,盐化意味着如果不破解哈希,就不可能确定两个用户是否拥有相同的密码,因为即使密码相同,不同的盐也会导致不同的哈希。

现代散列算法(如Argon2id、bcrypt和PBKDF2)会自动对密码进行盐化处理,因此在使用它们时不需要额外的步骤。

Peppering - 胡椒

相对于做菜来讲,每道菜需要使用的盐是不一样的,而“胡椒”是在每一道菜中作为一个调料品,可以固定添加。---译者著

除了加盐之外,还可以用胡椒来提供额外的保护层。如果攻击者只有对数据库的访问权限,比如说他们利用了SQL注入漏洞或获得了数据库的备份,那么它可以防止攻击者破解任何散列。胡椒策略不会以任何方式影响密码散列函数。

举例来讲,一种“胡椒”策略是像往常一样对密码进行哈希(使用密码哈希算法),然后在将密码哈希存储到数据库中之前,在原始密码哈希上使用HMAC(例如,HMAC-SHA256, HMAC-SHA512,取决于所需的输出长度),胡椒充当HMAC密钥。

  • 胡椒在存储的密码之间是共享的,而不是像盐一样唯一。
  • 与密码盐不同,“胡椒”不应该存储在数据库中。
  • “胡椒”是一个秘密,应该存放在“Secret Valuts”或HSM(硬件安全模块)中。
  • 像任何其他加密密钥一样,应该考虑对其做一个轮转策略控制。

Using Work Facators - 使用负载因子

负载因子是为每个密码执行哈希算法的迭代次数(通常,实际上是2^工作迭代)。负载因子通常存储在哈希输出中。它使得计算哈希的计算成本更高,这反过来降低了攻击者试图破解密码哈希的速度或增加了破解成本。

在选择负载因子时,要在安全性和性能之间取得平衡。虽然更高的负载系数使攻击者更难破解哈希,但它们会减慢验证登录尝试的过程。如果负载因子过高,则可能会降低应用程序的性能,攻击者可能会利用这一点通过使用大量登录尝试耗尽服务器的CPU来执行拒绝服务攻击。

理想的负载系数没有黄金法则——它取决于服务器的性能和应用程序上的用户数量。确定最佳负载因子需要在应用程序使用的特定服务器上进行实验。作为一般规则,计算哈希值应该少于1秒。

具有负载系数的一个关键优势是,随着硬件变得更强大和更便宜,它可以随着时间的推移而增加。

升级负载系数的最常见方法是等待用户下一次身份验证,然后用新的负载系数重新散列他们的密码。不同的散列将具有不同的负载因子,如果用户不重新登录到应用程序,则可能永远不会升级散列值。根据应用程序的不同,删除旧的密码散列并要求用户在下次登录时重置密码可能是合适的,以避免存储旧的和不太安全的密码哈希值。

算法 - Go语言实现测试

下列所有的测试数据都基于Mac mini M2芯片个人电脑计算得出。

测试参数为:-test.benchtime 10s

Argon2id

Password Hashing Competition:https://www.password-hashing.net/

RFC文档:https://datatracker.ietf.org/doc/html/rfc9106

其中OWASP推荐的几个参数配置如下,这些参数配置组合能够提供同等强度的密码哈希,其中的差别在于CPU和RAM的均衡使用:

M,最小内存,单位为KiB

t,最小迭代次数

p,并行度

1

46 * 1024(46M)

1

1

2

19 * 1024(19M)

2

1

3

12 * 1024(12M)

3

1

4

9 * 1024(9M)

4

1

5

7 * 1024(7M)

5

1

func Argon2id_46_1_1(password []byte) []byte {
  passwordHash := argon2.IDKey(password, random_salt(), 1, 46*1024, 1, 96)
  return passwordHash
}
func Argon2id_19_2_1(password []byte) []byte {
  passwordHash := argon2.IDKey(password, random_salt(), 2, 19*1024, 1, 96)
  return passwordHash
}
func Argon2id_12_3_1(password []byte) []byte {
  passwordHash := argon2.IDKey(password, random_salt(), 3, 12*1024, 1, 96)
  return passwordHash
}
func Argon2id_9_4_1(password []byte) []byte {
  passwordHash := argon2.IDKey(password, random_salt(), 4, 9*1024, 1, 96)
  return passwordHash
}
func Argon2id_7_5_1(password []byte) []byte {
  passwordHash := argon2.IDKey(password, random_salt(), 5, 7*1024, 1, 96)
  return passwordHash
}

测试结果:

OWASP优先推荐Argon2id作为密码哈希算法,并建议其最小的参数设置为

scrypt

RFC标准文档:https://datatracker.ietf.org/doc/html/rfc7914#page-12

https://www.cnblogs.com/flydean/p/15405264.html

其中OWASP推荐的几个参数配置如下,这些参数配置组合能够提供同等强度的密码哈希,其中的差别在于CPU和RAM的均衡使用:

N,CPU/内存耗费比

r,块大小

p,并行度

1

2^17 (128MiB)

8

1

2

2^16 (128MiB)

8

2

3

2^15 (128MiB)

8

3

4

2^14 (128MiB)

8

5

5

2^13 (128MiB)

8

10

func Scrypt_N2_13_R8_P10(password []byte) []byte {
  passwordHash, err := scrypt.Key(password, random_salt(), 1<<13, 8, 10, 96)
  if err != nil {
    panic(err)
  }
  return passwordHash
}
func Scrypt_N2_14_R8_P5(password []byte) []byte {
  passwordHash, err := scrypt.Key(password, random_salt(), 1<<14, 8, 5, 96)
  if err != nil {
    panic(err)
  }
  return passwordHash
}
func Scrypt_N2_15_R8_P3(password []byte) []byte {
  passwordHash, err := scrypt.Key(password, random_salt(), 1<<15, 8, 3, 96)
  if err != nil {
    panic(err)
  }
  return passwordHash
}
func Scrypt_N2_16_R8_P2(password []byte) []byte {
  passwordHash, err := scrypt.Key(password, random_salt(), 1<<16, 8, 2, 96)
  if err != nil {
    panic(err)
  }
  return passwordHash
}
func Scrypt_N2_17_R8_P1(password []byte) []byte {
  passwordHash, err := scrypt.Key(password, random_salt(), 1<<17, 8, 1, 96)
  if err != nil {
    panic(err)
  }
  return passwordHash
}

测试结果:

bcrypt

Paper: https://www.usenix.org/legacy/event/usenix99/provos/provos.pdf

https://php.watch/versions/8.4/password_hash-bcrypt-cost-increase

bcrypt的控制参数只有Cost,不同Cost之间的迭代次数会存在指数差别:

func Bcrypt_Cost10(password []byte) []byte {
  if len(password) > 72 {
    h := sha256.New()
    password = h.Sum(password)
  }
  passwordHash, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost)
  if err != nil {
    panic(err)
  }
  return passwordHash
}
func Bcrypt_Cost12(password []byte) []byte {
  if len(password) > 72 {
    h := sha256.New()
    password = h.Sum(password)
  }
  passwordHash, err := bcrypt.GenerateFromPassword(password, 12)
  if err != nil {
    panic(err)
  }
  return passwordHash
}
func Bcrypt_Cost14(password []byte) []byte {
  if len(password) > 72 {
    h := sha256.New()
    password = h.Sum(password)
  }
  passwordHash, err := bcrypt.GenerateFromPassword(password, 14)
  if err != nil {
    panic(err)
  }
  return passwordHash
}

测试结果:

pbkdf2

PBDKF介绍:https://www.cnblogs.com/flydean/p/15346657.html

RFC:https://datatracker.ietf.org/doc/html/rfc8018

其中OWASP推荐的几个参数配置如下:

算法

迭代次数

PBKDF2-HMAC-SHA1

1300000次迭代

PBKDF2-HMAC-SHA256

600000次迭代

PBKDF2-HMAC-SHA512

210000次迭代

func Pbkdf2_sha1_1300000(password []byte) []byte {
    return pbkdf2.Key(password, random_salt(), 1_300_000, 96, sha1.New)
}
func Pbkdf2_sha256_600000(password []byte) []byte {
    return pbkdf2.Key(password, random_salt(), 600_000, 96, sha256.New)
}
func Pbkdf2_sha512_210000(password []byte) []byte {
    return pbkdf2.Key(password, random_salt(), 210_000, 96, sha512.New)
}

测试结果:

总结

OWASP针对上述4种算法的推荐顺序如下:Argon2id > scrypt > bcrypt。如果需要满足FIPS-140(Federal Information Processing Standards)合规的情况下,使用PBKDF2-HMAC-SHA256,负载因子为600000次迭代是一个不错的选择。

在实际应用中,如果以PBKDF2-SHA256,迭代次数60W次作为baseline来看的话,我们单次验证密码的时间需要耗费0.2s左右,这种计算强度即能够一定程度上抵抗暴力攻击,又不会存在较大的被DOS攻击风险。同时结合“胡椒”方法,我们能够获得较强的密码攻击防御能力。

参考文档

  1. OWASP:https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
  2. Password Cryptography Specification: https://datatracker.ietf.org/doc/html/rfc8018
  3. scrypt - RFC7914:https://datatracker.ietf.org/doc/html/rfc7914#page-12
  4. RFC8018:https://datatracker.ietf.org/doc/html/rfc8018
  5. HMAC算法及计算流程介绍
  6. [译] 密码哈希的方法:PBKDF2,Scrypt,Bcrypt 和 ARGON2
  7. GO语言 crypto库针对password hash的支持:https://pkg.go.dev/golang.org/x/crypto
  8. Bcrypt Paper: https://www.usenix.org/legacy/event/usenix99/provos/provos.pdf
  9. Password Hashing Competition:https://www.password-hashing.net/
  10. RFC 9106:https://datatracker.ietf.org/doc/html/rfc9106
  11. https://www.cnblogs.com/flydean/p/15405264.html
  12. https://php.watch/versions/8.4/password_hash-bcrypt-cost-increase
目录
相关文章
|
2月前
|
缓存
基于Squid代理实现SSRF攻击物理防御
本文介绍一种通过Squid代理集群实现物理隔绝的SSRF防御方案。利用Private Link与NLB构建隔离网络,将用户URL请求转发至独立Squid集群处理,有效阻断内网探测与攻击,提升系统安全性。
149 4
|
1月前
|
存储 算法 安全
OpenSSL自签ECC算法私有证书链
本文介绍使用OpenSSL基于ECC算法(prime256v1)创建X.509v3格式的根CA与中间CA证书的完整流程,采用SHA256签名算法。涵盖私钥生成、配置文件编写、证书签发及验证步骤,并强调路径长度限制、算法安全性和私钥保护等关键注意事项,适用于构建符合IAM Roles Anywhere要求的可信证书体系。(238字符)
157 5
|
7月前
|
编解码 数据可视化 定位技术
Axure设计数字乡村可视化大屏:从布局到交互的实战经验分享
本文以Axure为工具,分享数字乡村可视化大屏的设计全流程。从需求分析到功能拆解,明确民生、经济、历史文化和空间分布四大维度,将大屏划分为左、中、右三区,涵盖10个统计模块。详细解析了各区域的图表设计与交互实现,如柱状图、环形图、时间轴等,并通过模块化设计和动态交互(如数据联动、弹窗详情)提升用户体验。最后总结避坑指南,强调数据可视化、交互友好及模块复用性,助力实现“数据驱动决策”的核心目标,推动乡村治理数字化转型。
403 132
|
机器学习/深度学习 算法 TensorFlow
深度学习基础:神经网络原理与构建
**摘要:** 本文介绍了深度学习中的神经网络基础,包括神经元模型、前向传播和反向传播。通过TensorFlow的Keras API,展示了如何构建并训练一个简单的神经网络,以对鸢尾花数据集进行分类。从数据预处理到模型构建、训练和评估,文章详细阐述了深度学习的基本流程,为读者提供了一个深度学习入门的起点。虽然深度学习领域广阔,涉及更多复杂技术和网络结构,但本文为后续学习奠定了基础。
572 5
|
安全 Ubuntu 测试技术
Acunetix v24.12 发布,新增功能概览
Acunetix v24.12 发布,新增功能概览
277 20
Acunetix v24.12 发布,新增功能概览
|
Linux 持续交付 Docker
掌握Docker:从入门到实践
Docker 是一个开源容器引擎,允许开发者将应用及其依赖打包成可移植的容器,在任意 Linux 机器上运行。本文从基本概念入手,详细介绍 Docker 的安装、基本操作、镜像构建及 Docker Compose 的使用,并通过实战案例展示如何部署 Web 应用、构建微服务架构及实现 CI/CD。通过学习,你将掌握 Docker 的核心功能,提升应用开发和部署效率。
|
安全 Java 数据安全/隐私保护
Stirling-PDF:一款优秀的开源PDF处理工具
Stirling-PDF是一个基于spring-boot开发的开源项目,旨在提供一个功能强大的基于Docker的本地托管PDF操作工具。它使您能够对PDF文件进行多种操作,包括拆分、合并、转换、重新组织、添加图片、旋转、压缩等。该本地托管应用最初由ChatGPT完全开发,并已发展成一个功能齐全的工具,可满足您的各种PDF需求。
6368 3
|
JSON 小程序 JavaScript
微信小程序开发笔记—底部导航栏tabar
本文介绍了微信小程序开发中底部导航栏的设计方法,步骤详细,非常适合初学的小伙伴!
1155 0
|
存储 安全 C++
UEFI vs Legacy:深入理解两种启动模式的区别
UEFI vs Legacy:深入理解两种启动模式的区别
5331 0
|
存储 Java C++
【python基础题】——知识点选择、填空、简答
【python基础题】——知识点选择、填空、简答