微服务架构下 数据脱敏 的设计与权衡
最近在搞微服务的数据脱敏,发现这事比想象中麻烦。原来单体应用一个拦截器能搞定,现在服务拆得七零八落,用户手机号在订单服务、用户服务、通知服务、日志里到处飞。你要脱敏,先得回答:在哪脱?脱给谁看?脱完还能不能用?这些问题不统一,最后就是每个服务各写一套正则,维护起来想死。
先别急着上工具,先把数据流画出来
我一开始也是直接找开源组件,结果发现没有一个能开箱即用。后来老老实实画数据流:哪些字段算敏感数据,从哪个服务产生,经过哪些服务,最后落到哪里。比如手机号,用户服务存明文,订单服务调它拿收货人手机号,通知服务发短信,日志里还打印。不同场景需求完全不一样:客服后台要看到完整号码,风控要能匹配,日志里绝对不能有。所以脱敏不是全局一刀切,而是按场景和身份来。
脱敏放在哪一层,这是个问题
常见选择有三个:网关、服务层、数据层。网关做统一脱敏,看着很爽,但内部服务调用绕过去就漏了,而且网关不知道业务语义,只能按字段名瞎猜。服务层知道上下文,但每个服务都要接 SDK,重复劳动,还容易出现“这个服务忘了加注解”的事故。数据层用数据库视图或者代理,最彻底,但性能开销和 SQL 兼容性够喝一壶。我的做法是对外 API 在网关做第一道,服务内部用注解/SDK 做第二道,数据库只做加密存储和静态脱敏。别指望一个地方全搞定。
静态脱敏和动态脱敏,别混着用
静态脱敏是改数据,比如测试库、备份、数据导出,脱完就回不去了,适合用不可逆的 hash 或者格式保留加密。动态脱敏是查询时实时处理,比如手机号显示成 138****1234,根据当前用户权限决定脱多少。动态脱敏要拦截 SQL 或 ORM,性能有开销,但灵活。我见过有人把静态脱敏规则配到动态查询里,结果缓存里全是脱敏后的数据,业务查询直接对不上,别问我怎么知道的。
sensitive:
phone:
strategy: PARTIAL
keepPrefix: 3
keepSuffix: 4
id_card:
strategy: HASH
salt: ${SENSITIVE_SALT}
日志、链路、消息队列:最容易漏的地方
日志是最容易翻车的地方。你代码里脱敏了,结果 log.info("user: {}", user) 直接把对象 toString 打出来,前功尽弃。我一般用 logback 的自定义 converter 或者结构化日志处理器,在输出前统一过一遍敏感字段。链路追踪也一样,span tag 里别塞完整请求体。消息队列更麻烦:生产端脱敏会丢信息,消费端脱敏可能已经落盘。我的建议是跨边界前脱敏,内部按需解密,别让 PII 在 MQ 里裸奔。
格式保留、令牌化、加密,怎么选
格式保留加密能保持长度和格式,测试和展示都方便,但密钥管理麻烦,性能也一般。令牌化用 token 替换真实值,安全强度高,但要多一个 token 服务,调用链和可用性风险都上来了。Hash 不可逆,适合去重和统计,但记得加盐,不然彩虹表教你做人。说实话,没有银弹,关键看你的威胁模型:是防内部人偷看,还是防数据库拖库,还是为了合规审计。别自己发明加密算法,也别把 base64 当脱敏。
权限和审计:脱敏不是遮羞布
脱敏不是把所有人都挡在外面,而是让该看的人看到该看的数据。客服要查订单,得能看到完整手机号,但要有审批和审计。风控要匹配黑名单,得能拿到原始值,但要有 break-glass 流程。如果脱敏只在服务端做,前端拿不到明文,那是对的;如果只在页面用 CSS 遮一下,那就是自欺欺人。还要保证一致性:同一个用户在订单服务和用户服务看到的脱敏结果得一样,不然客服会疯。我们最后抽了一个公共的脱敏策略服务,SDK 缓存策略,至少不会每个服务各写一套。
我的实践清单
- 先做数据分类分级,别上来就写正则。
- 统一字段标记,比如
@Sensitive(type = PHONE),让脱敏逻辑下沉到 SDK。 - 默认脱敏,按需申请,审批和审计留痕。
- 日志和链路强制脱敏,别靠开发自觉。
- 测试数据用静态脱敏,别直接拷生产库。
- 监控脱敏性能,动态脱敏别把 QPS 拖垮。
- 定期回扫,看有没有新服务绕过规则。
说到底,微服务下的数据脱敏就是个权衡:安全与可用性、统一与灵活、性能与合规。想清楚每个场景的边界,比堆工具重要得多。不然你脱了个寂寞,还多了几个半夜告警。
评论
还没有评论。
发表评论
提交后评论将经过自动审核,审核通过后公开展示。