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

密码安全那些事:从明文到 SHA-256 到 BCrypt,为什么一步步升级

核心问题:为什么密码不能直接存?

假设你的数据库里用户表长这样:

user_idusernamepassword
1zhangsan123456
2lisiabcdef

如果数据库被黑客拖走了(这叫"拖库"),所有用户的密码直接暴露。用户很多时候多个网站用同一个密码,一个库泄露,其他网站的账号也跟着遭殃。所以我们需要把密码"加工"一下再存,即使数据库泄露,黑客也拿不到原始密码。

第一步:MD5

MD5 是最早被广泛使用的哈希算法。哈希的意思就是:给一个输入,算出一个固定长度的"乱码",而且这个过程不可逆——你没办法从乱码反推出原始内容。

123456 → MD5 → e10adc3949ba59abbe56e057f20f883e abcdef → MD5 → e80b5017098950fc58aad83c8c14978e

现在数据库里存的是这串乱码,不是明文了。用户登录时,把他输入的密码也做一次 MD5,跟数据库里的比对,一样就说明密码正确。看起来安全了?问题来了。MD5 的致命缺陷:同样的输入永远得到同样的输出。黑客早就把常见密码的 MD5 值算好了,做成一张巨大的表,叫"彩虹表":

123456 → e10adc3949ba59abbe56e057f20f883e password → 5f4dcc3b5aa765d61d8327deb882cf99 qwerty → d8578edf8458ce06fbc5bb76a58c5ca4 ...几十亿条

拿到数据库里的哈希值,直接查表就能反查出原始密码。MD5 基本等于裸奔。

第二步:加盐(Salt)

既然问题出在"同样的密码产生同样的哈希",那我们在密码后面拼一段随机字符串再做哈希,这段随机字符串就叫"盐"(salt)。

用户密码:123456 随机盐值:x7k9m2 实际哈希的内容:123456x7k9m2

每个用户的盐值都不一样,所以即使两个用户密码都是 123456,存到数据库里的哈希值也完全不同:

user_idpassword_hashsalt
1a3f2b8c9...x7k9m2
27d1e4f5a...p3q8n1

这样彩虹表就废了,因为黑客不可能提前算出所有"密码+盐"的组合。

第三步:SHA-256

MD5 本身也有问题——它太快了,而且已经被发现存在碰撞(两个不同的输入可能算出相同的哈希值)。所以后来大家换成了更强的哈希算法,SHA-256 就是其中一个。SHA-256 输出 64 个十六进制字符,比 MD5 的 32 个更长,安全性更高:

123456 → SHA-256 → 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92

现在很多项目里用的就是 SHA-256 加盐。流程是:

存密码:SHA-256(明文密码 + 盐) → 存哈希值和盐 验密码:SHA-256(用户输入 + 数据库里的盐) → 跟数据库里的哈希值比对

这已经比 MD5 好很多了,但还有一个问题。SHA-256 的隐患:它还是太快了。现在的 GPU 一秒钟能算几十亿次 SHA-256。黑客拿到你的盐值后,可以暴力穷举——把所有可能的密码组合都试一遍,这叫"暴力破解"。密码短一点的,几小时就能破。

第四步:BCrypt

BCrypt 专门为密码存储设计,它解决了上面所有问题:

  • 自带盐:不用你自己生成和管理盐值,BCrypt 自动生成并嵌入到结果里
  • 故意很慢:BCrypt 有一个"工作因子"(cost factor),可以控制计算速度。默认是 10,意味着内部会做 2^10 = 1024 轮运算。调到 12 就是 4096 轮,越高越慢

慢有什么好处?你正常登录验证一次慢个 0.1 秒,用户完全无感。但黑客要暴力破解几十亿次,每次都要 0.1 秒,直接从几小时变成几百年。BCrypt 的输出长这样:

$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy

拆开看:

  • $2a$:算法版本
  • 10$:工作因子(cost = 10)
  • N9qo8uLOickgx2ZMRZoMye:盐值(自动生成的)
  • IjZAgcfl7p92ldGxad68LJZdL17lhWy:哈希值

盐和哈希值存在一起,验证的时候 BCrypt 自己会把盐提取出来,你什么都不用管。在 Spring Boot 里用起来非常简单:

// 加密 BCryptPasswordEncoder encoder = new BCryptPasswordEncoder(); String hash = encoder.encode("123456"); // 每次调用结果都不一样,因为盐是随机的 // 验证 boolean match = encoder.matches("123456", hash); // true boolean wrong = encoder.matches("654321", hash); // false

总结一下演进过程

明文存储 → 数据库泄露就全完了 ↓ MD5 → 彩虹表直接反查 ↓ MD5 + 盐 → 彩虹表废了,但 MD5 有碰撞问题 ↓ SHA-256 + 盐 → 更安全,但速度太快,暴力破解扛不住 ↓ BCrypt → 自带盐 + 故意慢,暴力破解成本极高

写到这里,整条演进线就很清晰了:每一次升级都不是为了炫技,而是上一代方案被实实在在地攻破了,才不得不往前走一步。明文太裸,MD5 被彩虹表打穿,加盐挡住了查表但挡不住暴力跑,SHA-256 快是优点也是缺点,最后 BCrypt 用"故意慢"这个反直觉的思路把问题兜住了。

下次面试官问你"密码怎么存",别直接蹦 BCrypt。从明文开始讲,一步步说清楚每个方案解决了什么、又留下了什么,比背一个标准答案有说服力得多。

http://www.jsqmd.com/news/494298/

相关文章:

  • C++多态:动态行为的核心奥秘
  • 数字电子技术题目
  • 2026年口碑好的纸尿裤工厂推荐:腰贴式纸尿裤/开合式纸尿裤口碑好的厂家推荐 - 品牌宣传支持者
  • 国际大厂德州仪器CC1101无线芯片反向电路学习指南:低功耗传输于ISM频段,模块丰富适合学习...
  • 苍穹外卖Day8 (地址簿 用户下单 功能支付)
  • Node.js 与 npm 的安装与配置(详细教程)
  • WeKnora快速部署攻略:开箱即用,打造个人专属知识问答机器人
  • ssm+java2026年毕设社区医院综合管理信息系统【源码+论文】
  • 三电平T型逆变器仿真模型:基于MATLAB Simulink的PWM控制与无中点电位不平衡控制
  • 小白友好:Qwen3-Reranker-0.6B本地部署,轻松提升RAG检索精度
  • Jmeter实战--压测 基本流程
  • 计算机毕业设计源码:Python基于Spark与Hive的酒店大数据分析与推荐系统 Django框架 Vue 可视化 Hadoop 爬虫 协同过滤推荐算法 民宿 客栈(建议收藏)✅
  • Windows系统下SIMetrix库管理终极方案:自定义中央仓库+环境变量配置教程
  • SpringAI大语言模型应用案例:智能问答系统开发
  • Python 深度学习代码调试,图像分割代码调试,图像分割代码 1、2D医学图像分割项目
  • SPI时序模式0-3终极图解:用逻辑分析仪抓取NRF24L01与SD卡的真实波形
  • Cesium新手避坑指南:从SHP到3D城市可视化的完整实战流程(附GitHub源码)
  • 用C语言程序解决两个简单问题
  • GitHub上的R包突然安装不上的原因之一
  • UniApp+SVGA跨端动画开发避坑指南:微信小程序与H5的兼容性处理大全
  • SpringAI集成OpenAI:从配置到调用实战
  • 手把手教你用MP2144搭建超低功耗单键开关机电路(含完整代码)
  • 矩转换矩阵
  • RePKG:Wallpaper Engine资源处理的高效工具与创新方案
  • 用AutoDL+SSH+VSCode搭建深度学习环境:保姆级连麦调试教程
  • 声纳方程实战指南:如何用Python模拟水下声波传播(附代码)
  • SpringAI大语言模型调用优化:性能提升技巧
  • 一键恢复 Redis 运行与 Grafana 监控(免修改 systemd 配置)
  • DID在算法策略评估中的5个常见误区及如何避免
  • MT5零样本改写真实体验:上传中文句子,秒获多个同义变体