FIDO# FIDO FIDO(Fast Identity Online,快速在线身份验证)是一套开源的**身份验证标准
发布时间:2026-10-07 | 浏览:1
FIDO(Fast Identity Online,快速在线身份验证)是一套开源的 身份验证标准 ,旨在替代传统的用户名 / 密码登录方式,解决密码易泄露、管理复杂等问题。它通过 公私钥加密技术 和 双因素认证 (如生物识别、安全密钥等)实现更安全、便捷的身份验证。
FIDO 联盟(FIDO Alliance)制定了两大核心标准:
FIDO UAF(Universal Authentication Framework,通用认证框架)
目标 :完全替代用户名 / 密码,支持生物识别(指纹、面部识别)、硬件密钥(如 USB Key)等认证方式。
目标 :完全替代用户名 / 密码,支持生物识别(指纹、面部识别)、硬件密钥(如 USB Key)等认证方式。
特点 : 客户端直接与服务端通信,无需依赖中间认证服务器。 支持跨设备、跨平台认证(如手机、电脑)。 典型应用:Windows Hello、Touch ID/Face ID 在部分网站的登录。
客户端直接与服务端通信,无需依赖中间认证服务器。
支持跨设备、跨平台认证(如手机、电脑)。
典型应用:Windows Hello、Touch ID/Face ID 在部分网站的登录。
FIDO U2F(Universal 2nd Factor,通用第二因子)
目标 :作为传统密码的 二次认证因子 ,增强现有登录流程的安全性(如 “密码 + 安全密钥” 组合)。
目标 :作为传统密码的 二次认证因子 ,增强现有登录流程的安全性(如 “密码 + 安全密钥” 组合)。
特点 : 需配合用户名 / 密码使用,提供双因素认证(2FA)。 通过 USB、NFC 或蓝牙连接硬件密钥(如 YubiKey、Google Titan Key)。 典型应用:GitHub、Google、Microsoft 等平台的二次认证。
需配合用户名 / 密码使用,提供双因素认证(2FA)。
通过 USB、NFC 或蓝牙连接硬件密钥(如 YubiKey、Google Titan Key)。
典型应用:GitHub、Google、Microsoft 等平台的二次认证。
FIDO 的工作原理主要通过以下步骤实现:
注册阶段 :用户将支持 FIDO 的设备(如生物识别设备、智能手机、安全密钥等)与在线服务进行注册。此时,设备会生成一对 唯一的公私钥对 ,公钥会被发送并存储到服务端,与用户账户相关联,而私钥则安全地保存在用户设备中。
认证请求阶段 :当用户尝试登录在线服务时,服务端会向用户设备发送一个唯一的挑战(Challenge)消息,该消息用于验证用户的身份。
认证响应阶段 :用户设备收到挑战消息后,会使用存储在本地的私钥对挑战进行签名(用户通过物理按键(或生物识别)确认操作),然后将签名后的响应消息发送回服务端。服务端收到响应后,会使用之前注册的公钥来验证签名的有效性。如果签名验证成功,服务端就会确认用户的身份合法,允许用户登录。
以某银行 APP 使用 FIDO 认证为例:
注册 :用户在银行 APP 中开启 FIDO 认证功能,将自己的手机(作为认证设备)与银行账户进行绑定。手机生成公私钥对,公钥上传至银行服务器,与用户账户信息关联存储,私钥则保存在手机的安全区域,如可信执行环境(TEE)或安全芯片中。
登录认证 :当用户下次登录银行 APP 时,APP 向服务器发送登录请求,服务器返回一个挑战消息给 APP。用户通过手机的指纹识别或其他生物识别方式(如面部识别)解锁手机中的私钥,然后手机使用私钥对挑战消息进行签名,并将签名结果发送给银行服务器。服务器收到签名后,用之前注册的公钥进行验证,若验证通过,则确认是合法用户,允许用户登录并访问账户信息和进行相关操作。
在这个案例中,FIDO 认证通过公私钥加密技术和用户的生物特征识别,实现了安全、便捷的登录认证过程,无需用户输入传统的用户名和密码,降低了密码泄露的风险,同时也提升了用户体验。
2.2 FIDO 认证流程中服务端与客户端交互细节
以下是关于 FIDO 认证流程中服务端与客户端交互细节 的详细解析,涵盖挑战消息内容、公私钥使用、签名生成及服务端验证逻辑:
在 FIDO 认证(如 FIDO2/WebAuthn)中, 服务端发起认证请求时会生成并发送挑战消息 ,用于验证用户的私钥持有性。以下是挑战消息的核心内容及作用:
extensions :用于传递自定义参数(如应用特定的权限校验信息)。
pubKeyCredParams :指定支持的公钥算法(如 ES256、RS256 等),通常在注册阶段而非认证阶段使用。
认证阶段不携带公钥 公钥在 注册阶段 已由客户端生成并存储在服务端(关联到用户的 userHandle 或 credential ID )。认证时,服务端通过 allowCredentials 字段指定对应的 credential ID ,客户端根据该 ID 找到本地存储的私钥进行签名。 优势:避免公钥在认证阶段传输,减少数据泄露风险。
公钥在 注册阶段 已由客户端生成并存储在服务端(关联到用户的 userHandle 或 credential ID )。认证时,服务端通过 allowCredentials 字段指定对应的 credential ID ,客户端根据该 ID 找到本地存储的私钥进行签名。
优势:避免公钥在认证阶段传输,减少数据泄露风险。
客户端(如浏览器或安全密钥设备)收到挑战消息后,需用 私钥对挑战内容进行签名 ,具体步骤如下:
客户端将挑战消息中的关键字段组合成 待签名的数据结构 ,通常包括: { "challenge" : "随机字节串" , "origin" : "服务端域名(如https://example.com)" , "rpId" : "example.com" , "userHandle" : "用户唯一标识" , "type" : "webauthn.get" // 表示认证操作 }
客户端将挑战消息中的关键字段组合成 待签名的数据结构 ,通常包括:
该数据会被序列化为 CBOR 格式 (FIDO 协议推荐的二进制格式)或 JSON,并进行哈希(如 SHA-256)生成摘要。
该数据会被序列化为 CBOR 格式 (FIDO 协议推荐的二进制格式)或 JSON,并进行哈希(如 SHA-256)生成摘要。
私钥存储位置 : 私钥通常存储在客户端的安全硬件(如 TPM 芯片、手机 SE 安全元件)或操作系统的安全密钥库(如 Windows Hello、macOS Keychain)中, 无法被应用直接读取 ,只能通过系统 API 调用私钥进行签名。
签名算法 : 根据注册阶段协商的算法(如 ES256、RS256)使用私钥对摘要进行签名,生成 签名结果 (通常为字节数组,如 ECDSA 的 r+s 值或 RSA 的签名值)。
客户端将签名结果与其他信息打包为 AuthenticatorAssertionResponse ,包含:
credentialId :认证器的唯一标识(关联服务端存储的公钥)。
signature :对挑战消息的签名值。
userHandle :用户标识(可选)。
clientDataJSON :客户端生成的附加数据(包含 challenge 、 origin 等,用于服务端校验一致性)。
服务端收到客户端的认证响应后,需通过以下步骤验证签名合法性及用户身份:
解码 clientDataJSON ,校验以下内容 : challenge 一致性 :确保客户端返回的 challenge 与服务端发送的完全一致,防止中间人篡改。 origin 合法性 :验证 origin 是否为服务端域名,防止跨站攻击。 type 正确性 :确保操作类型为 webauthn.get (认证),而非注册等其他操作。
解码 clientDataJSON ,校验以下内容 :
challenge 一致性 :确保客户端返回的 challenge 与服务端发送的完全一致,防止中间人篡改。
origin 合法性 :验证 origin 是否为服务端域名,防止跨站攻击。
type 正确性 :确保操作类型为 webauthn.get (认证),而非注册等其他操作。
根据 credentialId 从服务端存储中获取对应的 公钥 和 用户信息 (如 userHandle )。
若 credentialId 无效或未找到公钥,认证直接失败。
组装待验证数据 : 将 clientDataJSON 的哈希值与 credentialId 等信息组合,作为公钥验证的输入(具体格式需符合 FIDO 协议标准)。
组装待验证数据 : 将 clientDataJSON 的哈希值与 credentialId 等信息组合,作为公钥验证的输入(具体格式需符合 FIDO 协议标准)。
使用公钥验证签名 : 通过公钥算法(如 ES256、RS256)解签客户端返回的 signature ,验证其是否与待验证数据匹配。 若匹配成功,证明客户端确实持有对应的私钥(即用户通过生物识别或 PIN 码解锁了私钥存储设备)。 服务端的解签过程 仅用于验证签名是否由对应的私钥生成 ,而 无法获取私钥本身 。这是公钥密码学的核心特性之一 为什么解签无法反推私钥? (1)现代签名算法(如 ECDSA、EdDSA)均基于 单向陷门函数 ,即: 已知私钥时,生成签名是高效的(多项式时间); 仅已知公钥和签名时,反推私钥是计算上不可行的(指数级时间复杂度)。 例如:破解 256 位的 ECDSA(P-256 曲线)需要的计算量约为2128次操作,远超当前超级计算机的能力。 (2) 签名验证的输入限制 : 服务端解签时的输入是: 公钥( Q )、消息哈希( H ( m ))、签名( r , s )。 这些数据中 不包含任何私钥的直接或间接信息 (你可能会有疑问,签名不就是由私钥参与生成的吗?虽然如此,但是这不影响签名中不包含任何私钥的直接或间接信息,这是由签名算法的数学原理和单向函数特性实现的。),验证过程仅是对 “私钥是否参与签名生成” 的逻辑判断,而非数学上的逆运算。 实际应用中的安全性保障 私钥的物理隔离 : 在 FIDO 等安全协议中,私钥通常存储在 安全元件(SE) 或 可信执行环境(TEE) 中,无法被操作系统或应用层读取,从源头杜绝私钥泄露风险。 例如:手机的指纹模块、U 盾的硬件芯片均通过物理安全机制保护私钥。 对抗 “已知签名攻击” : 即使攻击者获取了多个(消息,签名)对,也无法通过数学方法反推私钥(因签名算法的抗碰撞性和单向性)。 这也是为什么 FIDO 允许同一私钥对不同挑战值生成签名,而不必担心私钥泄露。
通过公钥算法(如 ES256、RS256)解签客户端返回的 signature ,验证其是否与待验证数据匹配。
若匹配成功,证明客户端确实持有对应的私钥(即用户通过生物识别或 PIN 码解锁了私钥存储设备)。
若匹配成功,证明客户端确实持有对应的私钥(即用户通过生物识别或 PIN 码解锁了私钥存储设备)。
服务端的解签过程 仅用于验证签名是否由对应的私钥生成 ,而 无法获取私钥本身 。这是公钥密码学的核心特性之一
(1)现代签名算法(如 ECDSA、EdDSA)均基于 单向陷门函数 ,即:
已知私钥时,生成签名是高效的(多项式时间);
仅已知公钥和签名时,反推私钥是计算上不可行的(指数级时间复杂度)。
例如:破解 256 位的 ECDSA(P-256 曲线)需要的计算量约为2128次操作,远超当前超级计算机的能力。
(2) 签名验证的输入限制 : 服务端解签时的输入是:
公钥( Q )、消息哈希( H ( m ))、签名( r , s )。 这些数据中 不包含任何私钥的直接或间接信息 (你可能会有疑问,签名不就是由私钥参与生成的吗?虽然如此,但是这不影响签名中不包含任何私钥的直接或间接信息,这是由签名算法的数学原理和单向函数特性实现的。),验证过程仅是对 “私钥是否参与签名生成” 的逻辑判断,而非数学上的逆运算。
私钥的物理隔离 : 在 FIDO 等安全协议中,私钥通常存储在 安全元件(SE) 或 可信执行环境(TEE) 中,无法被操作系统或应用层读取,从源头杜绝私钥泄露风险。 例如:手机的指纹模块、U 盾的硬件芯片均通过物理安全机制保护私钥。
私钥的物理隔离 : 在 FIDO 等安全协议中,私钥通常存储在 安全元件(SE) 或 可信执行环境(TEE) 中,无法被操作系统或应用层读取,从源头杜绝私钥泄露风险。
例如:手机的指纹模块、U 盾的硬件芯片均通过物理安全机制保护私钥。
对抗 “已知签名攻击” : 即使攻击者获取了多个(消息,签名)对,也无法通过数学方法反推私钥(因签名算法的抗碰撞性和单向性)。 这也是为什么 FIDO 允许同一私钥对不同挑战值生成签名,而不必担心私钥泄露。
对抗 “已知签名攻击” : 即使攻击者获取了多个(消息,签名)对,也无法通过数学方法反推私钥(因签名算法的抗碰撞性和单向性)。
这也是为什么 FIDO 允许同一私钥对不同挑战值生成签名,而不必担心私钥泄露。
用户验证状态 :检查客户端是否通过生物识别或 PIN 码完成用户验证(FIDO 协议中的 userVerified 字段)。
认证器安全策略 :如要求认证器必须为平台认证器(内置在设备中)或跨平台认证器(如 USB 密钥)。
FIDO 认证与 JWT 的关系 : FIDO 认证完成后,服务端通常会生成 JWT 令牌 (包含用户身份、权限等声明),用于后续接口请求的身份验证。 JWT 的签名验证 :与 FIDO 的签名逻辑类似,服务端通过 JWT 的公钥(或对称密钥)验证令牌签名,解析声明(如 sub 用户标识、 exp 过期时间)以确认用户合法性。 区别 :FIDO 签名用于证明私钥持有性(硬件级认证),JWT 用于传输阶段的身份断言(令牌级验证)。
FIDO 认证与 JWT 的关系 :
FIDO 认证完成后,服务端通常会生成 JWT 令牌 (包含用户身份、权限等声明),用于后续接口请求的身份验证。
JWT 的签名验证 :与 FIDO 的签名逻辑类似,服务端通过 JWT 的公钥(或对称密钥)验证令牌签名,解析声明(如 sub 用户标识、 exp 过期时间)以确认用户合法性。
区别 :FIDO 签名用于证明私钥持有性(硬件级认证),JWT 用于传输阶段的身份断言(令牌级验证)。
通过上述流程,FIDO 协议确保了用户私钥的安全存储与签名验证的不可抵赖性,而服务端通过挑战 - 响应机制和公钥密码学,在无需存储用户密码的前提下完成强身份认证。
安全性更高 : 无需存储密码,避免拖库泄露风险。 私钥永不离开设备,黑客无法通过网络窃取。 依赖物理验证(如按键确认),防止钓鱼攻击。
无需存储密码,避免拖库泄露风险。
私钥永不离开设备,黑客无法通过网络窃取。
依赖物理验证(如按键确认),防止钓鱼攻击。
便捷性更强 : 一键认证(生物识别或硬件密钥),无需记忆密码。 支持多设备同步,跨平台使用(如手机扫码登录电脑)。
一键认证(生物识别或硬件密钥),无需记忆密码。
支持多设备同步,跨平台使用(如手机扫码登录电脑)。
标准化与兼容性 : 开源协议,支持主流浏览器(Chrome、Firefox、Edge)和操作系统(Windows、macOS、Linux)。 硬件密钥可通用(如 YubiKey 支持数千个应用)。
开源协议,支持主流浏览器(Chrome、Firefox、Edge)和操作系统(Windows、macOS、Linux)。
硬件密钥可通用(如 YubiKey 支持数千个应用)。
企业办公 : 替代传统 VPN 密码,通过安全密钥实现远程办公认证。 案例:微软 Azure AD、Okta 等身份管理平台支持 FIDO。
替代传统 VPN 密码,通过安全密钥实现远程办公认证。
案例:微软 Azure AD、Okta 等身份管理平台支持 FIDO。
互联网服务 : 社交媒体、云服务(如 Facebook、Dropbox)使用 FIDO 作为二次认证选项。
社交媒体、云服务(如 Facebook、Dropbox)使用 FIDO 作为二次认证选项。
移动设备 : 手机指纹 / 面部识别直接用于 APP 登录(如银行 APP、电商平台)。
手机指纹 / 面部识别直接用于 APP 登录(如银行 APP、电商平台)。
物联网(IoT) : 设备通过 FIDO 认证接入网络,避免弱密码导致的安全漏洞。
设备通过 FIDO 认证接入网络,避免弱密码导致的安全漏洞。
5 FIDO 与传统认证的对比
在服务端实现 FIDO 认证时,存储和架构设计需要考虑安全性、扩展性和性能。
6.1 FIDO服务端的核心存储内容
FIDO 认证的服务端需要存储以下关键信息(以安全且防篡改的方式):
内容 :用户注册时生成的公钥、凭证ID(Credential ID)、凭证类型等。
内容 :用户注册时生成的公钥、凭证ID(Credential ID)、凭证类型等。
存储方式 : 数据库 (如MySQL、PostgreSQL):存储结构化数据,支持索引查询。 加密存储 :公钥通常无需加密,但 凭证ID 可能需通过 HMAC 等方式防篡改。
数据库 (如MySQL、PostgreSQL):存储结构化数据,支持索引查询。
加密存储 :公钥通常无需加密,但 凭证ID 可能需通过 HMAC 等方式防篡改。
内容 :将 用户ID 与 公钥凭证 关联,可能包含多设备支持(如手机、安全密钥)。
内容 :将 用户ID 与 公钥凭证 关联,可能包含多设备支持(如手机、安全密钥)。
示例结构 : { "userId" : "user123" , // 用户ID "credentials" : [ // 公钥凭证 { "credentialId" : "BASE64_ENCODED_ID" , // 凭证ID "publicKey" : "BASE64_ENCODED_PUBLIC_KEY" , //公钥 "type" : "public-key" , // 凭证类型 "counter" : 0 , // 防重放攻击的签名计数器 "attestationFormat" : "packed" , "createdAt" : "2023-01-01T00:00:00Z" } ] }
作用 :记录每次认证的签名次数,用于检测重放攻击(如设备丢失后被滥用)。
存储要求 :原子性递增(需数据库或分布式锁支持)。
6.2 微服务场景下的架构设计
在微服务架构中,FIDO 认证通常有两种实现方式:
专门处理所有认证相关逻辑(包括 FIDO 注册、验证)。
提供标准化的认证接口给其他微服务调用。
解耦 :核心认证逻辑独立,便于维护和扩展。
安全隔离 :敏感数据(如公钥)集中管理,降低泄露风险。
复用性 :支持多业务线共享认证能力(如 APP、Web、IoT 设备)。
认证逻辑简单,与用户管理强耦合。
初期快速迭代,后续可能拆分为独立服务。
服务职责过重,可能影响性能和可维护性。
安全漏洞可能影响整个用户服务。
凭证ID 和 公钥 使用数据库字段级加密(如 AWS KMS、Vault)。
问题 :多实例环境下,签名计数器需保证原子性递增。
问题 :多实例环境下,签名计数器需保证原子性递增。
解决方案 : 数据库事务 :使用 UPDATE ... RETURNING 语句(如 PostgreSQL)。 分布式锁 :通过 Redis 或 ZooKeeper 实现锁机制。 缓存优化 :短期缓存计数器值,定期持久化到数据库。
数据库事务 :使用 UPDATE ... RETURNING 语句(如 PostgreSQL)。
分布式锁 :通过 Redis 或 ZooKeeper 实现锁机制。
缓存优化 :短期缓存计数器值,定期持久化到数据库。
FIDO 验证通过后,生成 JWT 或 OAuth 令牌,传递给其他服务。
FIDO 验证通过后,生成 JWT 或 OAuth 令牌,传递给其他服务。
示例流程: 1. 客户端完成FIDO认证 → 2. 认证服务生成JWT → 3. 客户端携带JWT访问其他微服务 → 4. 资源服务通过认证服务验证JWT有效性
通过 gRPC 或 REST API 暴露认证接口。
使用服务发现(如 Consul、Nacos)注册认证服务。
(1)通常用户在登录app的时候会进行FIDO认证,那么在用户后续使用app的过程中是不是不会触发FIDO认证了?
用户在登录 App 时完成 FIDO 认证后, 后续是否会再次触发 FIDO 认证取决于应用的安全策略和使用场景 。以下是详细分析:
用户通过 FIDO 认证后,服务端生成 短期会话令牌 (如 JWT),并返回给客户端。
客户端在令牌有效期内(如 30 分钟)访问应用时,只需携带令牌,无需重复进行 FIDO 认证。
普通功能访问(如浏览页面、查看非敏感数据)。
对安全性要求适中,希望提升用户体验的场景。
机制 : 首次认证时,服务端将用户设备标记为 “可信设备”(通过设备指纹、硬件 ID 等)。 后续在同一设备上访问时,默认信任设备,仅需验证会话令牌,无需重复 FIDO 认证。
首次认证时,服务端将用户设备标记为 “可信设备”(通过设备指纹、硬件 ID 等)。
后续在同一设备上访问时,默认信任设备,仅需验证会话令牌,无需重复 FIDO 认证。
风险 : 设备一旦被标记为可信,若设备丢失或被入侵,可能导致未授权访问。 需配合设备端安全措施(如设备加密、生物识别锁)降低风险。
设备一旦被标记为可信,若设备丢失或被入侵,可能导致未授权访问。
需配合设备端安全措施(如设备加密、生物识别锁)降低风险。
用户进行高风险操作时(如修改密码、转账、访问隐私数据),即使处于有效会话中,仍需重新进行 FIDO 认证。
用户在 App 中发起 “修改登录手机号” 操作 → 系统提示 “请验证身份” → 触发 FIDO 认证(指纹 / 安全密钥)。
敏感接口在服务端校验时,除验证会话令牌外,额外检查是否满足 “最近一次 FIDO 认证时间在 X 分钟内”。
若超时或未进行过认证,则拒绝请求并要求重新认证。
会话令牌过期(如超过 24 小时未操作),用户继续使用 App 时需重新登录并完成 FIDO 认证。
用户上午登录 App 后未操作,下午再次打开时 → 会话已过期 → 提示 “请重新登录” → 触发 FIDO 认证。
应用要求每次登录或关键操作必须通过 多因素认证 (如 FIDO + 短信验证码)。
即使会话未过期,用户切换设备或退出后重新登录时,仍需再次完成 FIDO 认证。
金融类 App、企业内部系统等高安全等级场景。
用户从新设备、新 IP 地址或异常地理位置访问时,系统检测到环境变更,强制要求 FIDO 认证。
用户在海外首次登录 App → 系统识别为异常环境 → 触发 FIDO 认证 + 短信验证。
安全与平衡体验: 普通操作通过会话令牌减少认证频率,提升体验; 敏感操作或环境风险升高时,强制 FIDO 认证以保障安全。
普通操作通过会话令牌减少认证频率,提升体验;
敏感操作或环境风险升高时,强制 FIDO 认证以保障安全。
动态风险评估: 结合设备指纹、行为分析(如登录频率、操作类型)动态决定是否需要重新认证。
结合设备指纹、行为分析(如登录频率、操作类型)动态决定是否需要重新认证。
合规性要求: 遵循行业标准(如 GDPR、金融合规),对特定操作强制要求强认证。
遵循行业标准(如 GDPR、金融合规),对特定操作强制要求强认证。
用户 A 使用银行 App 进行日常操作,流程如下:
首次登录 : 输入用户名 → 选择 “指纹登录”(FIDO 认证)→ 验证通过 → 获得 JWT(有效期 2 小时)。
输入用户名 → 选择 “指纹登录”(FIDO 认证)→ 验证通过 → 获得 JWT(有效期 2 小时)。
常规操作(无需重新认证) : 查看账户余额、交易记录 → 携带 JWT 访问接口 → 服务端验证令牌有效 → 返回数据。
查看账户余额、交易记录 → 携带 JWT 访问接口 → 服务端验证令牌有效 → 返回数据。
敏感操作(强制认证) : 发起 10 万元转账 → 服务端检测到操作风险 → 提示 “请验证身份” → 触发指纹认证(FIDO)→ 验证通过后执行转账。
发起 10 万元转账 → 服务端检测到操作风险 → 提示 “请验证身份” → 触发指纹认证(FIDO)→ 验证通过后执行转账。
会话过期 : 2 小时未操作 → JWT 过期 → 再次打开 App 时 → 提示 “请重新登录” → 需重新完成 FIDO 认证。
2 小时未操作 → JWT 过期 → 再次打开 App 时 → 提示 “请重新登录” → 需重新完成 FIDO 认证。
(2)客户端拿到FIDO认证后的JWT,是怎么进行存储的?
客户端拿到 FIDO 认证后的 JWT(JSON Web Token)后,存储位置的选择需平衡 安全性 与 可用性 。以下是常见的存储方案及其适用场景:
将 JWT 存储在 Cookie 中,并设置以下属性增强安全性:
HttpOnly : 防止 JavaScript 脚本访问,避免 XSS 攻击。
Secure : 仅通过 HTTPS 传输,防止中间人攻击。
SameSite : 限制跨域请求时 Cookie 的发送,防御 CSRF 攻击。
传统 Web 应用(如网站),依赖 Cookie 进行会话管理。
若未正确设置 HttpOnly ,可能被 XSS 攻击窃取。
存在 CSRF 风险(需配合 SameSite 或 CSRF 令牌)。
LocalStorage :长期存储(除非手动删除)。
SessionStorage :会话结束(浏览器关闭)后清除。
XSS 攻击 :JavaScript 可直接访问存储内容,易被窃取。
持久性风险 :LocalStorage 中的数据可能被恶意脚本长期利用。
仅在无敏感信息的场景使用,或对 JWT 进行加密存储(如结合 crypto.subtle API)。
存储方式 : 浏览器提供的异步数据库,可存储结构化数据(包括加密后的 JWT)。
相比 LocalStorage,IndexedDB 有更严格的访问控制和安全模型。
支持数据加密存储(需配合加密库如 crypto-js )。
仍需防范 XSS 攻击(若加密密钥泄露,数据可能被解密)。
使用 Keychain Services 存储 JWT,系统级加密保护,防逆向工程。
使用 Android Keystore 结合 SharedPreferences ,加密存储敏感数据。
支持生物识别(如指纹)解锁访问
iOS : UserDefaults (未加密,需自行加密 JWT 后存储)。
Android : SharedPreferences (未加密,需配合加密库如 Tink )。
明文存储存在风险,建议结合设备加密(如 Android 的 EncryptedSharedPreferences )。
机制: 将 JWT 的 签名部分 存储在 HttpOnly Cookie 中, 用户 ID 等非敏感信息 存储在 LocalStorage。 请求时,客户端合并两部分数据发送给服务端验证。
将 JWT 的 签名部分 存储在 HttpOnly Cookie 中, 用户 ID 等非敏感信息 存储在 LocalStorage。
请求时,客户端合并两部分数据发送给服务端验证。
优势: 降低 XSS 攻击风险(签名部分无法被 JavaScript 访问)。 提升可用性(非敏感信息可快速读取)。
降低 XSS 攻击风险(签名部分无法被 JavaScript 访问)。
提升可用性(非敏感信息可快速读取)。
机制: 客户端仅在内存中临时存储 JWT(如 Vuex/Redux 状态管理),不持久化。 关键操作时,重新从服务端获取认证状态或刷新令牌。
客户端仅在内存中临时存储 JWT(如 Vuex/Redux 状态管理),不持久化。
关键操作时,重新从服务端获取认证状态或刷新令牌。
优势: 避免本地存储风险,安全性最高。
避免本地存储风险,安全性最高。
劣势: 网络依赖强,频繁请求服务端影响性能。
网络依赖强,频繁请求服务端影响性能。
选择 JWT 存储位置时,需根据应用安全等级和使用场景权衡:
高安全性场景 (如金融 App):优先使用 iOS Keychain/Android Keystore + 加密存储。
Web 应用 :推荐 HttpOnly Cookie + SameSite 属性,结合 CSRF 防护。
临时会话 :使用内存存储或 SessionStorage,减少持久化风险。
无论采用哪种方案,均需配合服务端的 令牌验证机制 和 安全审计 ,形成完整的安全闭环
(3)在客户端已经登录(即拿到了FIDO认证的JWT)场景下,客户端访问服务端时,服务端是怎么验证这个用户是合法的,需要调用FIDO服务吗?
在客户端持有 FIDO 认证后的 JWT 访问服务端时,服务端的验证流程通常不需要再次调用 FIDO 服务,而是基于 JWT 本身的签名和声明信息进行验证。以下是详细的验证过程和技术细节:
服务端验证 JWT 的合法性主要基于以下步骤,无需依赖 FIDO 服务:
目的 :确保 JWT 未被篡改或伪造。
目的 :确保 JWT 未被篡改或伪造。
流程 : 服务端使用 签名密钥 (通常是私钥的公钥部分)对 JWT 的签名部分进行验证。 若签名验证失败,直接拒绝请求。
服务端使用 签名密钥 (通常是私钥的公钥部分)对 JWT 的签名部分进行验证。
若签名验证失败,直接拒绝请求。
常见验证项 : exp (Expiration Time) :检查令牌是否过期。 nbf (Not Before) :检查令牌是否已生效。 iss (Issuer) :验证令牌颁发者是否合法。 aud (Audience) :验证令牌接收方是否为当前服务。 自定义声明 :如用户角色、权限范围等。
exp (Expiration Time) :检查令牌是否过期。
nbf (Not Before) :检查令牌是否已生效。
iss (Issuer) :验证令牌颁发者是否合法。
aud (Audience) :验证令牌接收方是否为当前服务。
自定义声明 :如用户角色、权限范围等。
可选步骤 : 服务端可维护一个 令牌黑名单 (如用户主动登出时),验证 JWT 是否在黑名单中。 对于高安全场景,可结合 Refresh Token 机制定期刷新访问令牌。
服务端可维护一个 令牌黑名单 (如用户主动登出时),验证 JWT 是否在黑名单中。
对于高安全场景,可结合 Refresh Token 机制定期刷新访问令牌。
通常不需要 。服务端验证 JWT 的合法性是 独立于 FIDO 认证流程 的,原因如下:
JWT 自包含验证信息 : JWT 的签名部分已经包含了用户身份的证明(通过 FIDO 认证时生成),服务端只需验证签名即可确认用户身份。
性能考虑 : 每次请求都调用 FIDO 服务会显著增加延迟,降低系统吞吐量。
安全边界 : FIDO 认证仅用于初始登录,后续会话安全由 JWT 和服务端的会话管理机制负责。
例外情况 : 在以下场景中,服务端可能需要额外调用 FIDO 服务:
多因素认证(MFA) : 对于高风险操作(如转账),服务端要求用户再次通过 FIDO 验证(此时需调用 FIDO 服务)。
令牌绑定验证 : 服务端在 JWT 中嵌入了设备指纹或公钥 ID,需验证其与 FIDO 注册信息是否匹配。
使用 Refresh Token : JWT 设置较短的有效期(如 15 分钟),通过 Refresh Token 定期刷新,降低令牌被盗用的风险。
令牌绑定(Token Binding) : 将 JWT 与客户端设备的公钥或指纹绑定,验证请求来源的一致性。
分布式缓存 : 使用 Redis 等缓存存储已验证的 JWT 状态,减少重复验证开销。
审计日志 : 记录异常的验证失败请求,及时发现潜在的攻击行为。
服务端验证 JWT 的合法性主要依赖于 签名验证 和 声明解析 ,通常无需再次调用 FIDO 服务。FIDO 认证的作用是在用户登录时生成可信的身份凭证(JWT),而后续的会话管理则由服务端基于 JWT 独立完成。只有在特殊的安全场景(如多因素认证)下,才需要再次触发 FIDO 验证流程。
(4)服务端拿到一个JWT后,它是怎么知道这个JWT是合法的?
在服务端验证 JWT(JSON Web Token)的合法性时, 签名验证 和 声明解析 是两个核心环节。以下从技术原理、具体流程和安全逻辑三个维度详细解析,帮助理解服务端如何判断 JWT 是否合法。
JWT 的签名机制基于 密码学哈希算法 和 非对称加密 (或对称加密),其核心逻辑是:
生成签名时 : 客户端完成 FIDO 认证后,认证服务器(如 FIDO 服务)使用 私钥 (非对称加密场景)或 密钥 (对称加密场景),对 JWT 的 头部(Header) 和 载荷(Payload) 进行哈希计算,生成签名(Signature),并将其附在 JWT 末尾。 JWT 结构 : [Header] . [Payload] . [Signature]
生成签名时 : 客户端完成 FIDO 认证后,认证服务器(如 FIDO 服务)使用 私钥 (非对称加密场景)或 密钥 (对称加密场景),对 JWT 的 头部(Header) 和 载荷(Payload) 进行哈希计算,生成签名(Signature),并将其附在 JWT 末尾。 JWT 结构 :
验证签名时 : 服务端使用与签名生成时匹配的 公钥 (非对称加密)或 相同密钥 (对称加密),对收到的 JWT 的头部和载荷进行 相同的哈希计算 ,并将结果与 JWT 自带的签名进行比对。若一致,则证明 JWT 未被篡改;若不一致,则 JWT 非法。
服务端使用与签名生成时匹配的 公钥 (非对称加密)或 相同密钥 (对称加密),对收到的 JWT 的头部和载荷进行 相同的哈希计算 ,并将结果与 JWT 自带的签名进行比对。若一致,则证明 JWT 未被篡改;若不一致,则 JWT 非法。
以 非对称加密(RSA 算法) 为例:
提取 JWT 的三部分 : 将 JWT 按 . 拆分为 headerBase64 、 payloadBase64 、 signatureBase64 。
提取 JWT 的三部分 : 将 JWT 按 . 拆分为 headerBase64 、 payloadBase64 、 signatureBase64 。
解码头部 : 对 headerBase64 进行 Base64 解码,获取签名算法(如 RS256 表示使用 RSA-SHA256 算法)和密钥相关信息(如公钥 ID)。
解码头部 : 对 headerBase64 进行 Base64 解码,获取签名算法(如 RS256 表示使用 RSA-SHA256 算法)和密钥相关信息(如公钥 ID)。
获取验证密钥 : 根据头部中的信息(如公钥 ID),从 密钥管理服务 (如 AWS KMS、HashiCorp Vault)中获取对应的 公钥 。
获取验证密钥 : 根据头部中的信息(如公钥 ID),从 密钥管理服务 (如 AWS KMS、HashiCorp Vault)中获取对应的 公钥 。
重构签名 : 使用相同的哈希算法(如 SHA256),对原始的 headerBase64 和 payloadBase64 进行哈希计算,得到原始哈希值。
重构签名 : 使用相同的哈希算法(如 SHA256),对原始的 headerBase64 和 payloadBase64 进行哈希计算,得到原始哈希值。
验证签名 : 使用公钥对 JWT 自带的 signatureBase64 进行解密,得到签名哈希值。将其与步骤 4 的原始哈希值对比: 一致 → 签名有效,JWT 未被篡改。 不一致 → 签名无效,JWT 非法(可能被篡改或伪造)。
使用公钥对 JWT 自带的 signatureBase64 进行解密,得到签名哈希值。将其与步骤 4 的原始哈希值对比:
一致 → 签名有效,JWT 未被篡改。
不一致 → 签名无效,JWT 非法(可能被篡改或伪造)。
非对称加密的安全性 : 私钥仅由认证服务器(如 FIDO 服务)持有,公钥可公开分发。攻击者无法通过公钥反推私钥,因此无法伪造合法签名。
对称加密的风险 : 若使用对称加密(如 HMAC 算法),密钥需在服务端和认证服务器之间安全共享,一旦泄露会导致签名验证失效。因此生产环境更推荐非对称加密。
JWT 的载荷(Payload)包含一组键值对声明,用于描述 用户身份、权限、令牌时效 等信息。声明分为三类:
注册声明(Registered Claims) : 预定义的标准声明,如: exp (Expiration Time):令牌过期时间(Unix 时间戳)。 nbf (Not Before):令牌生效时间。 iss (Issuer):令牌颁发者(如 fido-auth-server.com )。 aud (Audience):令牌接收方(如目标服务 api.example.com )。
注册声明(Registered Claims) :
exp (Expiration Time):令牌过期时间(Unix 时间戳)。
nbf (Not Before):令牌生效时间。
iss (Issuer):令牌颁发者(如 fido-auth-server.com )。
aud (Audience):令牌接收方(如目标服务 api.example.com )。
公共声明(Public Claims) : 自定义声明,需在注册中心注册(非必需),如 user_id 、 role 。
公共声明(Public Claims) : 自定义声明,需在注册中心注册(非必需),如 user_id 、 role 。
私有声明(Private Claims) : 自定义的非公开声明,仅在发行方和消费方之间约定,如 department 。
私有声明(Private Claims) : 自定义的非公开声明,仅在发行方和消费方之间约定,如 department 。
服务端解析声明后,按以下规则验证 JWT 的有效性:
过期时间验证( exp ) : 检查当前时间是否超过 exp ,若超过则 JWT 过期,拒绝请求。
过期时间验证( exp ) : 检查当前时间是否超过 exp ,若超过则 JWT 过期,拒绝请求。
生效时间验证( nbf ) : 检查当前时间是否早于 nbf ,若早于则 JWT 尚未生效,拒绝请求。
生效时间验证( nbf ) : 检查当前时间是否早于 nbf ,若早于则 JWT 尚未生效,拒绝请求。
颁发者验证( iss ) : 确认 iss 是否为信任的认证服务器(如 FIDO 服务的域名),防止接收非法颁发者的令牌。
颁发者验证( iss ) : 确认 iss 是否为信任的认证服务器(如 FIDO 服务的域名),防止接收非法颁发者的令牌。
接收方验证( aud ) : 检查 aud 是否包含当前服务的标识(如服务端配置的 audience 列表),防止令牌被跨服务滥用。
接收方验证( aud ) : 检查 aud 是否包含当前服务的标识(如服务端配置的 audience 列表),防止令牌被跨服务滥用。
权限验证(自定义声明) : 根据业务需求验证用户权限,如: role 声明是否包含 admin 或 user 。 scopes 声明是否包含请求所需的权限(如 read:profile )。
根据业务需求验证用户权限,如:
role 声明是否包含 admin 或 user 。
scopes 声明是否包含请求所需的权限(如 read:profile )。
签名验证是基础 : 即使声明内容看似合法,若签名验证失败(如 JWT 被篡改),服务端会直接拒绝请求。
签名验证是基础 : 即使声明内容看似合法,若签名验证失败(如 JWT 被篡改),服务端会直接拒绝请求。
声明解析是补充 : 签名有效仅说明 JWT 未被篡改,但无法保证其时效性和权限合法性。例如: 攻击者可能窃取未过期的合法 JWT 用于越权访问,此时需通过声明中的 role 或 scopes 拦截请求。 合法 JWT 过期后,即使签名有效,也需通过 exp 声明拒绝请求。
签名有效仅说明 JWT 未被篡改,但无法保证其时效性和权限合法性。例如:
攻击者可能窃取未过期的合法 JWT 用于越权访问,此时需通过声明中的 role 或 scopes 拦截请求。
合法 JWT 过期后,即使签名有效,也需通过 exp 声明拒绝请求。