常见令牌化方案

聊令牌化方案,绕不开两个东西:PCI DSS 和 Vaultless vs Vaulted。

PCI DSS,说白了就是支付卡行业的数据安全标准。你只要是做支付相关的系统,就一定得跟它打交道。我这些年做过的合规项目,十个里有八个都跟PCI DSS脱不了干系。

至于Vaultless和Vaulted——这是两种截然不同的令牌生成思路。一个轻量,一个厚重;一个讲究算法,一个依赖存储。

先看PCI DSS令牌化

PCI DSS对持卡人数据(CHD)的保护有严格规定。其中一条很要命:禁止存储完整的磁条数据、CVV2和PIN。但业务又要跑,怎么办?令牌化就是答案。

你想想看,我拿卡号,生成一个令牌。令牌能用来做交易、查订单、走退款,但拿到令牌的人反推不出卡号。这不就合规了么?

核心逻辑:用令牌替代真实的PAN(Primary Account Number),让令牌在业务系统中流转,真实PAN牢牢锁在HSM或保险箱里。

什么是合格的令牌?我个人习惯看三点:

  • 不可逆——从令牌不能算出PAN,除非你有密钥和HSM。
  • 无意义——令牌本身不带卡BIN、校验位等敏感模式。
  • 域限制——令牌只能在发卡系统内部使用,出了域就是废纸。

我见过有的团队直接用PAN的SHA256哈希当令牌,结果被PCI审计直接卡住。为什么?哈希虽然不可逆,但暴力枚举卡号空间完全可行。你猜我后来怎么解决的?加了一个HSM派生密钥,再做HMAC。

Vaulted模式(又称有库令牌化)

Vaulted,就是建立一个中央令牌库。每个令牌对应一条记录,记录里存着PAN、有效期、令牌本身。你拿着令牌来查库,库给你真实PAN。这叫查表法。

我在项目中使用过最常见的Vaulted方案:

  • 数据库里一张_mapping表,字段大概就是_id, pan, expires_at, created_at。
  • 真实PAN加密存储,解密密钥放在HSM里。
  • 令牌本身可以是随机UUID或雪花ID。
-- 这是个简化的_mapping表结构
CREATE TABLE _mapping (
    _id    VARCHAR(64) PRIMARY KEY,
    pan_enc     VARBINARY(256) NOT NULL,   -- HSM加密后的PAN
    pan_fp      VARCHAR(64) NOT NULL,      -- 用于查重的指纹,HMAC-SHA256
    created_at  TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

这种方案的好处很明显:

  • 令牌生成快——纯粹是一次INSERT。
  • 令牌可以取消——删掉映射记录,令牌立刻失效。
  • 支持大规模并发——只要数据库扛得住。

但坑也有。我曾经遇到一个支付平台,令牌库每天写入上千万条记录,到最后单表查询慢得不行。你以为加索引就能解决?错了,令牌映射查的是令牌字段,你建的索引覆盖不了所有查询模式。最后还是分表+Redis缓存才救回来。

另一个隐患是库的可用性。令牌库一旦挂了,所有依赖令牌的业务都得停。那你问我怎么办?一般做法是主从、多活、加上本地缓存。

Vaultless模式(又称无库令牌化)

Vaultless,就是不依赖中央数据库。令牌本身是一个自包含的数据结构,里面藏了PAN的部分信息,但加了强加密和完整性校验。拿到令牌后,用HSM的密钥解密,就能还原出PAN。

说白了,Vaultless就是加密+格式保留加密(FPE)的变体。常见的两种算法:

  • FF1 / FF3-1——NIST标准,保留明文格式和长度。
  • AES-CBC + 签名——非保留格式,但更安全。
# 一个伪代码级别的Vaultless令牌生成流程
def generate_(pan, hsm_key_id):
    # 1. 从HSM获取派生密钥
    dek = hsm.derive_key(hsm_key_id, pan_bin[:4] + pan_bin[-4:])
    # 2. 用FPE加密PAN中间部分
    encrypted_pan = fpe_encrypt(pan, dek, tweak=txn_id)
    # 3. 拼接令牌结构:前6位明文 + 加密中间 + 后4位明文 + 签名
     = pan[:6] + encrypted_pan[6:-4] + pan[-4:]
     += hmac(dek, )[:8].hex()
    return 

这种方案的优势:

  • 零存储——令牌自己不依赖数据库。
  • 低延迟——脱令牌只需要HSM解密,一次网络交互就够。
  • 无限扩展——没有库容量上限。

不过也别高兴太早。Vaultless的弱点也很要命:

  • 令牌不是随机生成的——同卡号在不同时段生成的令牌,可能部分重复。
  • 一旦密钥泄露——所有令牌都能被逆向。
  • 吊销困难——你不能删除某个令牌,只能更新密钥,但更新密钥会让所有旧令牌失效。

两张方案的对决

我直接列个对比表,你看哪个更适合你的场景:

维度 Vaulted(有库) Vaultless(无库)
存储依赖 强依赖令牌库 无需持久化存储
令牌可逆性 查库即可还原 必须走HSM解密
吊销令牌 删除映射记录即可 需要全量密钥替换
性能瓶颈 数据库IO HSM加解密
合规难度 库中加密PAN需定期轮换 密钥管理和算法审核
常见场景 电商、订阅、退款场景 收单、快捷支付、低延迟交易

我个人的选型建议

做选型其实没那么玄乎。我一般这样判断:

  • 如果我的业务需要频繁吊销令牌(比如风控要求阻止某张卡),我会选Vaulted。
  • 如果我的业务追求极致吞吐,而且令牌数量可以预见会爆炸式增长,我会选Vaultless。
  • 如果我的客户对数据主权有强制要求(比如泛金融监管),Vaulted更可控。

我记得在做一个东南亚支付项目时,当地监管要求令牌必须与卡号对等可溯源。我们落地的是Vaulted + HSM加密库+定期审计。另一个北美电商项目就不一样了,直接用Vaultless,因为商家只关心支付完成,不关心令牌能不能取消。你看,场景不同,选择完全不同。

一张图看懂两种模式

下面这张SVG,我画了Vaulted和Vaultless的完整流程对比。节点用深色区分,箭头代表数据流向,说明框放关键差异。你仔细看,应该能一眼看出两种模式的本质区别:

Vaulted 与 Vaultless 令牌化流程对比 Vaulted vs Vaultless 流程对比 Vaulted(有库) 应用请求令牌 传入 PAN HSM 加密 PAN 返回密文 插入令牌库 生成随机令牌 返回令牌给业务 业务存储令牌 取令牌时查库 HSM 解密还原 PAN Vaultless(无库) 应用请求令牌 传入 PAN HSM 派生密钥 使用 FPE 算法 生成自包含令牌 无数据库依赖 返回令牌给业务 令牌即数据载体 取令牌时解密 HSM + 派生密钥还原 关键差异:Vaulted 依赖中央存储,令牌可随时吊销;Vaultless 依赖密钥派生,令牌自含信息但吊销成本高 选择取决于业务需求:合规严选 Vaulted,高性能选 Vaultless

最后聊几句安全红线

不管选哪种,有几个红线不能碰:

  • 令牌不能包含PAN明文片段——除了格式保留令牌的固定段(比如前6后4),其余必须混乱或加密。
  • 密钥必须独立——令牌生成密钥和生产加密密钥不能共用。我曾经在一个项目中发现测试环境和生产环境用了同一个HSM密钥组,差点出事故。
  • 审计日志要全——谁、什么时间、通过什么接口、请求了多少次令牌还原,全都要记下来。

重中之重:无论Vaultless还是Vaulted,都不能让自己的HSM密钥变成明文配置文件。我见过最夸张的是一位开发者把HSM密钥硬编码在Gradle配置里上传到GitHub,三分钟内就被爬虫抓走。你想想看,这得多吓人。

好了,关于常见令牌化方案的核心内容基本就这些。PCI DSS是底线,Vaulted和Vaultless是工具,你得清楚自己的业务场景,才不会选错。