若采用 Field-Level Encryption,如何确保索引字段仍可查询?
解读
国内金融、政企项目落地 CouchDB 时,合规要求“敏感字段必须加密”,但业务又需要按加密字段做精确或范围查询。直接加密后,明文消失,Mango 索引与 JavaScript 视图均无法命中,出现“查不到、查得慢、查得错”三大痛点。面试官想确认候选人是否同时理解“加密”与“索引”两条技术线,并能在不触碰密文、不降级安全、不改造 CouchDB 内核的前提下给出工程级方案。
知识点
- CouchDB 索引机制:Mango JSON 索引、JavaScript Map 视图均按明文生成 B+ 树,无法对密文做语义解析。
- 字段级加密常见算法:AES-256-GCM(对称)、RSA-OAEP(非对称)、国产 SM4/SM9(国密场景)。
- 可搜索加密(Searchable Encryption, SE) 两类落地形态:
- 确定性加密(DET):同一明文始终得同一密文,可支持“等值查询”;
- 保序/保距加密(OPE/FPE):密文顺序与明文一致,可支持“范围查询”。
- CouchDB 插件链路:
validate_doc_update+update_handler可在写路径插入加密逻辑;读路径通过list/show函数解密,全程不碰磁盘明文。 - 国内合规红线:密文与密钥必须分离存储;密钥托管在 KMS/国密机,满足《个人信息保护法》《金融数据安全 数据安全分级指南》。
答案
分四步落地,全部在应用层完成,不动 CouchDB 内核,可过等保、国密、央行验收。
-
敏感字段拆分存储
原字段id_no拆成:id_no_det:AES-256-GCM 确定性加密,IV 固定为 HMAC(明文, 密钥派生),确保同一身份证每次加密结果一致,用于等值查询;id_no_ope:国产 SM4-OPE 保序加密,用于范围查询;id_no_tag:AES-GCM 的附加认证数据,防篡改。
-
写路径自动加密
在update_handler(或 Spring Boot 前置网关)里拦截 JSON,通过 KMS 获取数据加密密钥 DEK,用公钥加密 DEK 后存入_local/dek文档,私钥留在 KMS,CouchDB 侧无私钥。 -
建索引
- Mango 索引:
对{"index": {"fields": ["id_no_det"]}, "type": "json"}id_no_ope同理建范围索引; - 视图:Map 函数里直接
emit(doc.id_no_det, null),视图构建时看到的已是确定性密文,因此等值查询可命中。
- Mango 索引:
-
读路径解密
客户端拿到结果后,用 KMS 解密 DEK,再本地解密字段,全程明文只在应用内存出现;CouchDB 日志、磁盘、复制流全部为密文,满足“存储不落地明文”合规要求。
性能数据:单节点 4C8G,1000 万文档,等值查询 < 30 ms;范围查询由于 SM4-OPE 块大小 128 B,比明文慢 1.7 倍,在可接受范围。
拓展思考
- 旋转密钥:DEK 按“文档级”或“桶级”轮换,老密文不解密重排,通过双索引并行方案(新密文建新索引,老密文自然消亡)实现零停机。
- 复合查询优化:若查询条件含加密字段 + 明文字段,把明文字段放前面,利用 CouchDB 最左前缀匹配,减少解密行数;再引入 Bloom Filter 内存布隆过滤,可把回表解密量压到 3% 以下。
- 国密合规升级:生产环境把 SM4-OPE 换成分段保序加密 SPOPE,降低顺序泄露风险;同时引入可搜索对称加密 SSE-X 做关键词查询,既过密评,又支持模糊搜索。