当前位置: 首页 > news >正文

硬编码Salt问题及修复方案

一、硬编码Salt的介绍

1. 什么是Salt?

在密码学中,Salt是一段随机生成的数据,用于作为哈希函数(如MD5, SHA-256, bcrypt)的额外输入。其主要目的是防御彩虹表攻击

  • 没有Salt的情况: 如果两个用户的密码相同,他们的密码哈希值也会完全相同。攻击者可以预先计算一个巨大的“明文-密文”对照表(即彩虹表),通过比对哈希值快速反推出原始密码。

  • 有Salt的情况: 即使两个用户密码相同,因为为他们生成的Salt不同,最终的哈希值也完全不同。攻击者必须为每个Salt单独制作一张彩虹表,极大地增加了攻击成本和难度。

2. 什么是硬编码Salt?

硬编码Salt是指将Salt值直接以明文形式写入应用程序的源代码、配置文件或二进制文件中。

示例(极不安全的做法):

java

// 硬编码Salt示例(Java)
public class UserService {// 盐值被直接写在代码里private static final String HARDCODED_SALT = "MyStaticSalt123!";public String hashPassword(String password) {// 将硬编码的盐和密码拼接后进行哈希String saltedPassword = password + HARDCODED_SALT;return sha256(saltedPassword);}
}

python

# 硬编码Salt示例(Python)
import hashlibHARDCODED_SALT = b"my_fixed_salt"def hash_password(password):salted_password = password.encode() + HARDCODED_SALTreturn hashlib.sha256(salted_password).hexdigest()

二、硬编码Salt的危害

硬编码Salt虽然提供了“加盐”这个动作,但它极大地削弱了Salt的安全价值,带来严重风险:

  1. Salt不再是“随机”的

    • 安全性降低: Salt的核心是“唯一性”和“随机性”。硬编码Salt使得所有用户都使用同一个Salt,攻击者只需要为这一个Salt制作一张彩虹表即可攻击所有用户。这与不加盐相比,安全性提升非常有限。

    • 等同于全局密码: 这个硬编码的Salt就像一个全局密钥,一旦泄露,所有用户的密码哈希都处于危险之中。

  2. 违反安全原则

    • 缺乏隔离性: 如果多个应用程序或服务使用相同的硬编码Salt,攻击者成功破解一个系统,就等于破解了所有使用相同Salt的系统。

    • 无法轮换: Salt应该像密码一样可以定期更换。但硬编码Salt被写死在代码中,更换它意味着需要修改代码、重新编译、部署,并且更换后所有现有用户的密码哈希都将失效,导致所有用户必须重置密码,这在实践中几乎不可行。

  3. 代码泄露导致Salt泄露

    • 源代码可能会通过版本控制系统(如Git)、供应链攻击、内部人员泄露等方式公开。一旦源代码泄露,攻击者就直接拿到了Salt。

    • 即使代码不泄露,攻击者也可以通过反编译二进制文件轻松提取出硬编码的Salt值。

  4. 合规性问题

    • 现代安全标准和法规(如OWASP, GDPR, PCI DSS)明确反对或禁止使用硬编码的密钥和Salt。使用硬编码Salt会使应用程序无法通过安全审计。

三、修复方案

修复的核心思想是:为每个密码使用唯一、随机、高强度的Salt,并安全地存储它

方案一:最佳实践 —— 使用现代哈希算法(首选)

绝对不要自己实现加密逻辑! 应该使用经过严格审计、专门为密码哈希设计的算法。这些算法内部已经完美地处理了Salt的生成和存储。

  • 推荐算法: bcryptscryptArgon2 (是Password Hashing Competition的获胜者)。

  • 这些算法的优点

    1. 内置随机Salt: 每次调用都会自动生成一个强大的随机Salt。

    2. 包含工作因子: 可以调整计算成本(CPU/内存),以抵御暴力破解。

    3. 一体化输出: 算法的哈希输出结果是一个单一的字符串,其中自动包含了Salt、工作因子和最终的哈希值。你只需要存储这个字符串即可,验证时算法会自动从中解析出Salt。

修复示例(Python - bcrypt):

python

import bcrypt# 哈希密码
def hash_password(password):# gensalt() 会自动生成一个随机盐,rounds=12是工作因子hashed = bcrypt.hashpw(password.encode(), bcrypt.gensalt(rounds=12))return hashed # 这个hashed里已经包含了salt和哈希结果# 验证密码
def verify_password(plain_password, stored_hashed_password):# bcrypt.checkpw 会自动从stored_hashed_password中提取salt并对明文密码进行哈希比对return bcrypt.checkpw(plain_password.encode(), stored_hashed_password)

存储方式
在数据库的用户表中,你只需要一个列(如password_hash)来存储bcrypt.hashpw()返回的整个字符串。它看起来像这样:
$2b$12$sR/MsD9sFQSQRHNLsDrCF.BnW9bEBCFWQneU.NAqLpZYplQyMlLQy

  • $2b$ 代表算法标识符。

  • 12$ 代表工作因子。

  • sR/MsD9sFQSQRHNLsDrCF. 这前22个字符就是随机生成的Salt

  • 剩余部分才是真正的哈希值。

方案二:如果必须使用传统哈希(如SHA系列),请遵循以下原则

注意:这通常是遗留系统的过渡方案,新项目强烈建议使用方案一。

  1. 为每个用户生成随机Salt
    使用加密安全的随机数生成器(CSPRNG)为每个用户在注册或重置密码时生成唯一的Salt。

    java

    import java.security.SecureRandom;
    import org.apache.commons.codec.binary.Hex;public byte[] generateSalt() {SecureRandom random = new SecureRandom();byte[] salt = new byte[16]; // 至少16字节random.nextBytes(salt);return salt;
    }
  2. 安全地存储Salt

    • 将每个用户的Salt与他们的密码哈希值分开但同时存储在数据库中。

    • 通常的做法是在用户表中增加一个salt列,与password_hash列并列。

    • 切勿将Salt与密码哈希拼接后存储在一个字段里,除非你使用方案一中那种自带格式的算法。

  3. 进行哈希计算

    java

    public String hashPassword(String password, byte[] salt) throws NoSuchAlgorithmException {MessageDigest digest = MessageDigest.getInstance("SHA-256");digest.reset();digest.update(salt); // 先放入盐byte[] hashedBytes = digest.digest(password.getBytes(StandardCharsets.UTF_8));return Hex.encodeHexString(hashedBytes);
    }
  4. 验证过程
    验证时,从数据库中取出该用户的saltpassword_hash,然后用同样的方法对用户输入的明文密码和取出的salt进行哈希计算,最后比较结果是否与数据库中的password_hash一致。

方案三:管理全局Pepper

有时,除了 per-user Salt,还会使用一个Pepper

  • Pepper: 类似于一个全局的、秘密的Salt。它被添加到所有用户的密码中,但不存储在数据库里。

  • 与Salt的区别: Salt是唯一的、非保密的(存于DB);Pepper是统一的、保密的(不存于DB)。

正确做法

  1. 不能硬编码: Pepper必须作为一个环境变量配置文件(从安全的密钥管理服务如HashiCorp Vault、AWS Secrets Manager、Azure Key Vault中读取),绝不能硬编码

  2. 与Per-User Salt结合使用: 首先使用方案一或方案二为每个用户加盐哈希,然后再用全局Pepper进行二次加密(例如使用HMAC)。

总结与行动步骤

  1. 审计代码: 立即检查项目中所有处理密码哈希的地方,查找硬编码的Salt或密钥。

  2. 迁移到现代算法: 将密码哈希逻辑替换为 bcrypt、scrypt 或 Argon2。这是最彻底、最安全的解决方案。

  3. 密码重置: 如果之前使用的是硬编码Salt,必须将所有现有用户的密码作废,并要求他们在登录时重置密码。因为旧的哈希值是在不安全的Salt下生成的。在用户下次登录成功后,用新的安全方法重新哈希其密码。

  4. 安全存储: 如果使用传统方法,确保Salt是随机的、 per-user的,并安全地存储在数据库中。

  5. 废除硬编码: 彻底从代码中删除任何硬编码的密码、密钥、Salt等敏感信息。

记住安全领域的黄金法则:永远不要自己发明加密算法,并使用经过实践检验的、标准的库来处理安全问题。


文章转载自:

http://6LaQlvW4.qtxwb.cn
http://bRbmADJh.qtxwb.cn
http://GJ9q2OsQ.qtxwb.cn
http://U590o0z2.qtxwb.cn
http://gmPGsa0M.qtxwb.cn
http://4Ws7rzjQ.qtxwb.cn
http://NC9IYdx3.qtxwb.cn
http://GmNhlDVq.qtxwb.cn
http://x4j5ooHb.qtxwb.cn
http://LQ2fXzkJ.qtxwb.cn
http://qSpzeAuc.qtxwb.cn
http://chbe8kl7.qtxwb.cn
http://qnOQaqvK.qtxwb.cn
http://KI6JtnQG.qtxwb.cn
http://BjGiK1gv.qtxwb.cn
http://c6KavlpD.qtxwb.cn
http://gMjSqYGS.qtxwb.cn
http://m8lzNcBZ.qtxwb.cn
http://2zCXaw0D.qtxwb.cn
http://l2FMGi4D.qtxwb.cn
http://jUk0pJQz.qtxwb.cn
http://qfaZsY6T.qtxwb.cn
http://1AB3mgAc.qtxwb.cn
http://7r6snkGK.qtxwb.cn
http://qNdoYSFJ.qtxwb.cn
http://jaSqQHJR.qtxwb.cn
http://yI2WFuWp.qtxwb.cn
http://hkGhquoj.qtxwb.cn
http://cJnMeXQy.qtxwb.cn
http://AfxAM86D.qtxwb.cn
http://www.dtcms.com/a/376015.html

相关文章:

  • 随笔一些用C#封装的控件
  • 9月9日星期二今日早报简报微语报早读
  • Python快速入门专业版(十五):数据类型实战:用户信息录入程序(整合变量、输入与类型转换)
  • GEO与SEO,GEO 是什麼?SEO + AI = GEO 生成式搜尋引擎優化 全解析
  • Asp .Net Core 系列:Asp .Net Core 集成 Hangfire+MySQL
  • 如果服务端有数据更新,浏览器缓存同时也没有过期,如何直接使用最新的数据
  • 使用java编写一个基础的彩票抽奖程序
  • 算法题 Day5---String类
  • 【靶场练习】--DVWA第二关Command Injection(命令执行)全难度分析
  • 什么是Adobe Analytics?数据驱动营销的关键工具​
  • 使用Docker搭建MaxKB智能体平台
  • 【链表】3.重排链表(medium)
  • 免费!离线!免安装!Windows文件夹隐藏工具
  • 联邦学习及其相关创新SCI辅导
  • 466章:Python Web爬虫入门:使用Requests和BeautifulSoup
  • ES8集群部署与使用-zookeeper集群部署与使用
  • Nginx 优化与防盗链配置指南
  • 【数据结构】栈详解
  • 力扣周赛困难-3677. 统计二进制回文数字的数目(需要一定推理的经典二分)
  • 【硬件-笔试面试题-77】硬件/电子工程师,笔试面试题(知识点:滤波电路中截止频率的计算)
  • CUDA编程13 - 测量每个Block的执行时间
  • 仓颉编程语言青少年基础教程:特殊数据类型Unit类型和Nothing类型)
  • AFSim2.9.0学习笔记 —— 3、Wizard平台类型与ArkSIM平台介绍
  • 基于LTE标准的MIMO-OFDM仿真程序
  • 814章:Python Web爬虫入门:使用Requests和BeautifulSoup
  • 硬件开发(5)—ARM汇编
  • leetcode16(盛最多水的容器)
  • 《面向高速三维表面成像的微型深度学习轮廓术》论文总结
  • 基于Java的图书管理系统的设计与实现
  • 【Qt跬步积累】—— 初识Qt