认证基础:什么是身份认证?密码认证、多因素认证(MFA)的原理与对比

聊数据安全,第一个绕不开的话题就是——你是谁?

你想想看,系统怎么知道坐在屏幕前的是你本人,而不是隔壁老王,或者某个爬虫脚本?这就是身份认证要解决的核心问题。说白了,认证就是「验明正身」的过程。

我刚开始做安全那会儿,觉得认证不就是输个密码嘛,简单得很。直到后来参与了一个金融项目,被各种绕过密码的攻击打得措手不及……嗯,从那以后我再也不敢小看这个环节了。

什么是身份认证?

身份认证,英文叫 Authentication,简称 AuthN。它的目标只有一个:确认用户声称的身份是否属实。

你输入「我是张三,密码是 123456」,系统就去查数据库,看密码对不对得上。对得上,就放你进去;对不上,就拒绝。就这么个逻辑。

但这里有个关键点:认证 ≠ 授权。认证是确认你是谁,授权是确认你能干什么。很多新手把这两个混在一起,结果权限体系一塌糊涂。我见过一个团队,把用户角色直接写在认证逻辑里,每次改权限都要改认证代码……那叫一个酸爽。

一句话总结:认证解决「你是不是你」的问题,授权解决「你能做什么」的问题。两者必须分开设计。

认证的三个要素

业内把认证手段归纳为三类,也叫「认证三要素」:

  • 你知道什么(Something you know)——密码、PIN码、密保答案
  • 你拥有什么(Something you have)——手机、硬件令牌、智能卡
  • 你是什么(Something you are)——指纹、人脸、虹膜、声纹

单因素认证只用其中一类,多因素认证(MFA)则组合两类或三类。我个人的习惯是:但凡涉及资金或核心数据,至少上两个因素。

密码认证:最古老也最脆弱的方式

密码认证属于「你知道什么」。用户注册时设置一个密码,系统存它的哈希值(注意,不是明文!)。登录时,用户再输一次,系统比对哈希值是否一致。

流程大致如下:

  1. 用户输入用户名 + 密码
  2. 系统对密码做哈希运算(比如 bcrypt、scrypt、Argon2)
  3. 拿哈希结果去数据库比对
  4. 匹配则通过,不匹配则拒绝
// 伪代码示例:密码验证
function authenticate(username, password) {
    // 从数据库取出该用户的密码哈希
    storedHash = db.getPasswordHash(username);
    // 对输入的密码做同样的哈希运算
    inputHash = hash(password);
    // 比对
    return storedHash === inputHash;
}

注意:千万不要存明文密码!我曾经接手过一个遗留系统,数据库里密码全是明文……当时我后背都凉了。哈希算法建议用 bcrypt 或 Argon2,别再用 MD5 或 SHA-1 了,那玩意儿在 GPU 面前跟纸糊的一样。

密码认证的优点很明显:简单、成本低、用户习惯成熟。但缺点同样致命:

  • 容易被猜解——弱密码、默认密码、社工库
  • 容易被窃取——钓鱼、键盘记录、中间人攻击
  • 用户管理负担重——记不住、到处重复用、忘记后重置流程繁琐

我记得有个统计说,超过 80% 的数据泄露事件都跟弱密码或密码泄露有关。所以,单靠密码已经不够用了。

多因素认证(MFA):加一把锁

MFA 的核心思想很简单:不要只依赖一个因素。就算密码被偷了,攻击者没有你的手机,照样进不去。

常见的 MFA 组合方式:

组合方式 示例 安全等级
密码 + 短信验证码 登录时输入密码,再输入手机收到的 6 位码 中等(短信可能被拦截)
密码 + TOTP Google Authenticator、Authy 生成的动态码 较高
密码 + 硬件密钥 YubiKey、FIDO2 安全密钥 高
生物特征 + 密码 指纹 / 人脸 + 密码 高(但生物特征不可重置)

TOTP(基于时间的一次性密码)是目前最流行的 MFA 方案之一。它的原理是:

  1. 服务端和客户端共享一个密钥(注册时生成)
  2. 双方都基于当前时间戳(通常 30 秒一个窗口)计算哈希
  3. 客户端展示 6 位数字,服务端验证是否匹配
// TOTP 生成核心逻辑(简化版)
function generateTOTP(secretKey, timeStep = 30) {
    // 当前时间戳除以时间步长,得到计数器值
    counter = Math.floor(Date.now() / 1000 / timeStep);
    // 用 HMAC-SHA1 对计数器进行签名
    hmac = HMAC-SHA1(secretKey, counter);
    // 取最后 4 位作为偏移量,提取 31 位整数
    offset = hmac[19] & 0xf;
    code = (hmac[offset] & 0x7f) << 24 |
           (hmac[offset+1] & 0xff) << 16 |
           (hmac[offset+2] & 0xff) << 8 |
           (hmac[offset+3] & 0xff);
    // 取模 10^6,得到 6 位数字
    return code % 1000000;
}

个人建议:如果条件允许,优先用 TOTP 或硬件密钥,而不是短信验证码。短信有被 SIM 卡劫持或 SS7 协议攻击的风险。我在一个金融项目里就遇到过攻击者通过运营商漏洞拦截验证码的案例,后来全部换成了 TOTP。

密码认证 vs MFA:对比一览

维度 密码认证 MFA
因素数量 1 个(你知道什么) 2 个或以上
用户体验 简单快捷 多一步操作,略繁琐
安全性 低(易泄露、易猜解) 高(即使密码泄露,仍有第二道防线)
实施成本 低 中等(需集成 TOTP 或短信网关)
常见攻击面 钓鱼、撞库、暴力破解 SIM 卡劫持、中间人攻击(较难)
适用场景 低风险系统、内部测试环境 核心业务、金融、数据管理后台

MFA 的避坑指南

MFA 不是银弹。我见过不少团队上了 MFA 之后反而出了新问题:

  • 恢复流程没设计好——用户丢了手机,进不了系统,客服被投诉到崩溃。一定要提供备用恢复码或备用邮箱验证。
  • MFA 疲劳攻击——攻击者不断触发 MFA 推送,用户烦了随手一点「允许」,就中招了。建议限制推送频率,或者要求用户输入动态码而不是点确认。
  • 生物特征不可重置——指纹、人脸一旦泄露,你没法像换密码一样换一张脸。所以生物特征更适合作为第二因素,而不是唯一因素。

我曾经踩过的坑:给一个内部管理系统上了 MFA,但忘了把管理员账号也纳入 MFA 范围。结果攻击者通过社工拿到了管理员密码,直接绕过了所有防护……从那以后,我定了一条铁律:所有账号,包括管理员,必须走 MFA。

认证体系的核心逻辑图

下面这张图展示了从用户请求到认证决策的完整链路,以及 MFA 在其中扮演的角色:

身份认证核心流程(含 MFA) 用户发起登录请求 密码验证(因素1) 密码正确? 拒绝访问 MFA 验证(因素2) MFA 通过? 认证通过,发放令牌 密码错误或 MFA 失败 均直接拒绝,不留情面

从图上可以看得很清楚:密码和 MFA 是串联关系。密码是第一道门,MFA 是第二道门。两道门都过了,系统才发令牌。任何一道失败,直接拒绝,不给任何提示——这是安全设计的基本原则。

小结

身份认证是整个数据安全体系的入口。密码认证虽然简单,但已经不足以应对今天的威胁环境。MFA 通过叠加多个因素,大幅提升了攻击成本。

我个人建议的落地策略是:

  • 内部系统:密码 + TOTP(成本低,安全性够)
  • 面向客户的核心业务:密码 + 硬件密钥或生物特征(体验与安全平衡)
  • 管理员通道:必须 MFA,且推荐硬件密钥(最高安全等级)

记住一点:没有的安全,只有不断增加的攻击成本。MFA 就是那个让攻击者觉得「算了,换个目标吧」的筹码。