开发笔记:如何消除秘钥数据对RPC负荷、日志、系统安全的伤害?

0x01. 秘钥数据特征

以我们系统的数据表levy_merchant_relation为例,该数据表存储的是商户服务商关联关系。下面是与之对应的 LevyMerchantRelation 实体类结构,除了包括商户与服务商相关字段,还包括HTTP接口通信的秘钥和口令,如RSA公私钥、加密秘钥。

LevyMerchantRelation ​{id, merId, merName, levyId, levyName, levyMerId, levyMerName, interfaceKey, publicKey, privateKey, loginPassword, memo, createTime, updateTime}

本文要说的是这些秘钥字段数据。

这些秘钥数据,尤其是RSA秘钥,有两个特征:

  1. 是比较长的字符串。
  2. 属于系统隐私数据。

这些秘钥数据会对系统带来如下伤害:

  1. 对于微服务架构系统来说,这些数据通过RPC传输,一来泄露了隐私数据,致使系统存在安全风险;其次,这些数据急剧增大RPC数据传输的payload(响应结果包含秘钥数据的list接口或page接口,所带来的影响尤其明显)。
  2. 程序日志方面,这些数据打印在日志文件里,一来泄露了隐私数据,致使系统存在安全风险;其次,这些大字符串数据会使日志体积变大。再者,当我们在通过分析日志来排查系统问题时,这些大字符串往往比较碍眼,会降低我们排查问题的效率。

0x02. 如何解决秘钥数据对系统产生的伤害?

*⬮*解决对RPC传输的伤害 ←RPC传输层的数据隔离

1. DTO职责分离设计:一分为二

作为RPC传输的LevyMerchantRelationDTO,其结构不能直接是LevyMerchantRelation实体类的"副本",而要排除掉秘钥字段,毕竟,90%的调用方是使用relation关系,而不需要秘钥数据。另外,对于那些需要秘钥数据的功能,如页面的CRUD,则定义单独的秘钥DTO。是的,仅包含秘钥字段。

LevyMerchantRelation ​{id, merId, merName, levyId, levyName, levyMerId, levyMerName, interfaceKey, publicKey, privateKey, loginPassword, memo, createTime, updateTime}

⬇ ⬇ ⬇

LevyMerchantRelationDTO ​{id, merId, merName, levyId, levyName, levyMerId, levyMerName, memo, createTime, updateTime}

ApiSecretDTO ​{interfaceKey, publicKey, privateKey, loginPassword}

一分为二后的两个DTO,一个仅包含业务字段,一个仅包含秘钥字段,职责清晰,边界分明,各司其职。

2. RPC接口的精细化设计

对应的服务接口应做相应拆分,确保"按需知密"的安全原则。

  • 对于查询接口,单独定义个获取秘钥的接口(如果需要的话),其他接口均返回 LevyMerchantRelationDTO。
  • 新增/修改接口则同时包含LevyMerchantRelationDTO和ApiSecretDTO这2个参数。通过RPC接口层面彻底隔离,把伤害降低至0。

*⬮*解决对日志的伤害

有了上面RPC传输层的数据隔离,LevyMerchantRelationDTO 本身不用考虑了。我们需要考虑的,是entity和ApiSecretDTO等包含秘钥的POJO。

我们在kibana日志平台经常看到长长的秘钥串,像金灿灿的油菜花田里一株突兀的、傲慢的绿草,它肆意摇曳,便将整片田野的和谐平衡,轻易撕开一道口子。

如何解决呢?

取决于我们打印日志的方式。

不外乎如下2种:

  • 方式一:log.info("XX服务商】-查询企业账户信息,接口入参:relation={}", levyMerchantRelation);
  • 方式二:log.info("XX服务商】-查询企业账户信息,接口入参:relation={}", JSON.toJSONString(levyMerchantRelation));

针对方式一,重写entity等模型类的 toString() 方法,在拼接字符串时去掉秘钥field。

复制代码
import lombok.Data;
 
@Data
public class LevyMerchantRelation {
    ...
 
    @Override public String toString() {
        return "LevyMerchantRelation{" +
                ...
                ", interfaceKey='" + StringUtils.length(interfaceKey) + " characters'" +
                ", publicKey='" + StringUtils.length(publicKey) + " characters'" +
                ", privateKey='" + StringUtils.length(privateKey) + " characters'" +
                ...
                '}';
    }
}

针对方式二,则要说道说道了。一种办法是不用json序列化,改用方式一。但难免未来在系统迭代中又会出现这种方式的log.info。我们也不能将此纳入到团队编码规范里。毕竟,在log中查看经json序列化后的对象字符串,更利于肉眼识别关键信息。由此,JSONLog出现了。

JSONLog是什么?是一个自定义的工具类,是一个在IDE中键入"JSON"就能联想出来的工具类。

JSONLog的定义如下,它在将对象进行序列化时,过滤掉了 publicKey、privateKey、password等秘钥字段关键字。

复制代码
package com.sby.common.json;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SimplePropertyPreFilter;
import com.google.common.collect.Sets;
 
public class JSONLog {
    private static final SimplePropertyPreFilter filter = new SimplePropertyPreFilter();
 
    static {
        filter.getExcludes().addAll(Sets.newHashSet(
                "privateKey", "publicKey", "password", "secret",
                "accessKey", "secretKey", "token",
                "interfaceKey", "encryptKey", "apiKey"
        ));
    }
 
    /**
     * 安全序列化,自动排除 敏感字段(RSA公钥还是长字符串)
     */
    public static String toJSONString(Object obj, String... fields) {
        if (obj == null) return "null";
 
        return JSON.toJSONString(obj, filter);
    }
}

然后,开发团队内部广而告之,相信会比其他解决方案要好。

0x03. 总结

这种从数据模型、接口设计到工具支持的全方位思考,正是构建安全、高性能系统的关键所在。

彼时的2025年5月份,消费券系统开始立项研发,在程序设计阶段,我们要为系统的两个关键参与者------消费企业 与 核销企业------设计数据表结构,我主张为两者分别创建各自的表,至于两者的营业资质信息,则存储到一个共同的营业资质表。看来,通过这种数据隔离方式的数据结构设计,亦可以有效规避这些营业资质信息在上述两方面对系统产生的伤害。 ------------设若 levy_merchant_relation 这张表 不包含秘钥字段(关联关系与关联关系的秘钥分开存储),那么,就不存在 LevyMerchantRelation"因秘钥数据而对系统产生伤害"的事情。------------anyway,从数据设计源头规避风险,也是很重要的一件事。

ref: ⬮ 如何在程序日志中不打印LevyMerchantRelation中的privateKey、publicKey这些大字符串field?程序日志优化:精准捕获与日志分级,践行数字低碳20231130-调用上海银行timeout,哐哐报错。日复一日月复一月,我们视而不见。近几日日志文件动辄60~70G,我们的系统往往以日志量取胜。 < 摘自公司内部confluence-wiki>

相关推荐
CoreTK芯通康EMC整改1 分钟前
PCB 信号回流路径 EMC 失效深度解析:原理、误区与量产级整改方案
网络·经验分享·安全·emc整改案例·emc整改
asdfg12589634 分钟前
运营商的路由器到路由器之间是如何传输的
网络·智能路由器
疯狂打码的少年10 分钟前
【程序语言与编译】正规式与有限自动机的等价转换
笔记
代码中介商13 分钟前
HTTP进化史:从1.0到3.0的核心变革
网络·网络协议·http
是上好佳佳佳呀17 分钟前
【LangChain|Day03】LangChain 链式调用 Chains 笔记
笔记·langchain
ZStack开发者社区23 分钟前
ZSTACK · 答客问 | 什么时候该升级,什么时候再等等
网络·云计算
MXsoft61832 分钟前
**混合云统一监控实践:私有云+公有云的一体化运维方案**
运维·网络·数据库
天南散修38 分钟前
MT7916驱动中802.11转换为802.3
linux·网络·驱动开发·wifi·802.11
其实防守也摸鱼1 小时前
无线网络安全--10 规避WLAN验证之挫败MAC地址限制
网络·智能路由器·php·教程·虚拟机·wlan·无线网络安全
xiaoxiaoxiaolll1 小时前
《Nature Communications》亮点文章:自供电双窄带OPD如何实现1.25 Mbps下的抗窃听光通信?
网络