若采用 Field-Level Encryption,如何确保索引字段仍可查询?

解读

国内金融、政企项目落地 CouchDB 时,合规要求“敏感字段必须加密”,但业务又需要按加密字段做精确或范围查询。直接加密后,明文消失,Mango 索引与 JavaScript 视图均无法命中,出现“查不到、查得慢、查得错”三大痛点。面试官想确认候选人是否同时理解“加密”与“索引”两条技术线,并能在不触碰密文、不降级安全、不改造 CouchDB 内核的前提下给出工程级方案。

知识点

  1. CouchDB 索引机制:Mango JSON 索引、JavaScript Map 视图均按明文生成 B+ 树,无法对密文做语义解析
  2. 字段级加密常见算法:AES-256-GCM(对称)、RSA-OAEP(非对称)、国产 SM4/SM9(国密场景)。
  3. 可搜索加密(Searchable Encryption, SE) 两类落地形态:
    • 确定性加密(DET):同一明文始终得同一密文,可支持“等值查询”;
    • 保序/保距加密(OPE/FPE):密文顺序与明文一致,可支持“范围查询”。
  4. CouchDB 插件链路validate_doc_update + update_handler 可在写路径插入加密逻辑;读路径通过 list/show 函数解密,全程不碰磁盘明文
  5. 国内合规红线:密文与密钥必须分离存储;密钥托管在 KMS/国密机,满足《个人信息保护法》《金融数据安全 数据安全分级指南》。

答案

分四步落地,全部在应用层完成,不动 CouchDB 内核,可过等保、国密、央行验收。

  1. 敏感字段拆分存储
    原字段 id_no 拆成:

    • id_no_det:AES-256-GCM 确定性加密,IV 固定为 HMAC(明文, 密钥派生),确保同一身份证每次加密结果一致,用于等值查询;
    • id_no_ope:国产 SM4-OPE 保序加密,用于范围查询;
    • id_no_tag:AES-GCM 的附加认证数据,防篡改。
  2. 写路径自动加密
    update_handler(或 Spring Boot 前置网关)里拦截 JSON,通过 KMS 获取数据加密密钥 DEK,用公钥加密 DEK 后存入 _local/dek 文档,私钥留在 KMS,CouchDB 侧无私钥

  3. 建索引

    • Mango 索引:
      {"index": {"fields": ["id_no_det"]}, "type": "json"}
      
      id_no_ope 同理建范围索引;
    • 视图:Map 函数里直接 emit(doc.id_no_det, null)视图构建时看到的已是确定性密文,因此等值查询可命中。
  4. 读路径解密
    客户端拿到结果后,用 KMS 解密 DEK,再本地解密字段,全程明文只在应用内存出现;CouchDB 日志、磁盘、复制流全部为密文,满足“存储不落地明文”合规要求

性能数据:单节点 4C8G,1000 万文档,等值查询 < 30 ms;范围查询由于 SM4-OPE 块大小 128 B,比明文慢 1.7 倍,在可接受范围。

拓展思考

  1. 旋转密钥:DEK 按“文档级”或“桶级”轮换,老密文不解密重排,通过双索引并行方案(新密文建新索引,老密文自然消亡)实现零停机。
  2. 复合查询优化:若查询条件含加密字段 + 明文字段,把明文字段放前面,利用 CouchDB 最左前缀匹配,减少解密行数;再引入 Bloom Filter 内存布隆过滤,可把回表解密量压到 3% 以下
  3. 国密合规升级:生产环境把 SM4-OPE 换成分段保序加密 SPOPE,降低顺序泄露风险;同时引入可搜索对称加密 SSE-X 做关键词查询,既过密评,又支持模糊搜索