七、网络安全-企业数据脱敏

文章目录

  • 前言
  • 一、数据脱敏方法
  • 二、企业脱敏方案
    • 1. 数据库脱敏方案
    • 2. 历史数据脱敏
    • 3. 具体实现
  • 三、日志脱敏方案
  • 四、输出脱敏


前言

数据脱敏

  随着用户对个人隐私数据的重视和法律法规的完善,数据安全显得愈发重要。一方面可以加强权限管理,减少能够接触数据的人员以及导出数据加强审批。另一方面,还需要从技术上对用户隐私数据进行脱敏处理,提高数据的安全性。


一、数据脱敏方法

  数据脱敏方法有很多种,大致可以按照以下进行分类:

  • 隐藏法: 只显示敏感信息的部分内容,其他部分进行遮挡,比较常见使用星号替代。这种方式日常比较多见,比如手机号,银行卡号等只显示后面和后面几位,好处是虽然只是部分内容显示,但足够提供有效信息,同时不会暴露完整数据。
  • 混淆法: 对原有数据截断、替换、隐藏、数字进行随机移位,使得原有数据完全失真或者部分失真,混淆真假。
  • 加密:通过加密密钥和算法对敏感数据进行加密得到密文,密文可见但是完全没有可读意义,是脱敏最彻底的方法。其中对称加密还能密钥解密可以从密文恢复原始数据。比如密码保存采用非对称加密,手机号存储时采用对称加密。

  用户的敏感数据包含姓名、电话号码、身份证、银行卡号、电子邮件、家庭住址、登录密码等等。需要考虑数据的敏感程度、数据安全要求以及实际业务使用场景选择合适的脱敏方法。Hutool包里面提供了许多常用的脱敏方法。

二、企业脱敏方案

  企业如何实现脱敏?
  我们先来看典型的系统数据交互链路,数据需要经过数据库、后端应用、前端(PC\移动端)。

系统数据交互链路

  • 数据库侧: 数据库保存了原始数据,有权限人员可以查看数据和导出数据。
  • 后端应用内: 后端应用中会打印相关日志,数据通过日志存储下来。通过日志,能够得到原始数据。
  • 应用输出: 前端能够从后端读取到原始数据。

1. 数据库脱敏方案

根据业务具体要求选择合适脱敏方法。

  1. 脱敏地点可以在应用中手动脱敏,当然这种方法不常用,改动点多对业务侵入大。
  2. 另外一种方案在ORM框架中修改sql实现,其中mybatis框架为java后端系统中最常用的框架。mybatis自带拦截器扩展,允许在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
  • Executor: 拦截执行器的方法,例如 update、query、commit、rollback 等。可以用来实现缓存、事务、分页等功能。
  • ParameterHandler: 拦截参数处理器的方法,例如 setParameters 等。可以用来转换或加密参数等功能。
  • ResultSetHandler: 拦截结果集处理器的方法,例如 handleResultSets、handleOutputParameters 等。可以用来转换或过滤结果集等功能。
  • StatementHandler: 拦截语句处理器的方法,例如 prepare、parameterize、batch、update、query 等。可以用来修改 SQL 语句、添加参数、记录日志等功能。

Mybatis执行流程

2. 历史数据脱敏

数据库脱敏另外一个问题是历史数据问题。历史原因最开始的技术方案保存明文,所以脱敏时需要做到平滑脱敏。要做到平滑脱敏,可按照如下流程:

  • 新增脱敏字段: 在源表上新增脱敏字段。
  • 数据双写: 源字段和脱敏字段都写入数据。
  • 历史数据迁移: 历史数据迁移,刷入脱敏字段。
  • 读取脱敏字段: 从脱敏字段读取数据返回。
  • 清空源字段: 确保所有流程都正确的情况下,清空源字段。

3. 具体实现

本文mybatis实现数据库加解密为例。

1.表里面新增脱敏字段。

示例中脱敏新字段格式规范为源字段添加Encrypt后缀。vo里面添加脱敏注解标记

public class Employee {private Long id;private String name;@EncryptTagprivate String mobile;private String mobileEncrypt;private String email;private double salary;
} 

2.实现自定义拦截。

/***
** 加密拦截
***/
@Intercepts({@Signature(type = Executor.class,method = "update",args = {MappedStatement.class, Object.class}
), @Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}
), @Signature(type = Executor.class,method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}
)})
public class EncryptPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {MappedStatement mappedStatement = (MappedStatement)invocation.getArgs()[0];Object param = invocation.getArgs()[1];PluginService.encrypt(invocation, param);return invocation.proceed();}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}
}/***
** 解密拦截
***/
@Intercepts({@Signature(type = ResultSetHandler.class,method = "handleResultSets",args = {Statement.class}
)})
public class DecryptPlugin implements Interceptor {@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object result = invocation.proceed();if (result != null && result instanceof List) {this.decrypt(((List) result).iterator());}return result;}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {}private void decrypt(Iterator iterator) throws Throwable {while(iterator.hasNext()) {Object object = iterator.next();PluginService.decrypt(object);}}
}

3.实现sql修改,完成加解密逻辑。

public class PluginService {private static final Logger LOGGER = LoggerFactory.getLogger(PluginService.class);private static final Map<String, List<Field>> ENCRYPT_TAG_FIELDS = new ConcurrentHashMap();public static void encrypt(Invocation invocation, Object object) throws Throwable {if (object.getClass().isArray()) {int length = Array.getLength(object);if (length <= 0) {return;}for (int i = 0; i < length; ++i) {encryptSingleObject(Array.get(object, i));}} else if (object instanceof Collection) {Collection collection = (Collection) object;Iterator itr = collection.iterator();while (itr.hasNext()) {Object item = itr.next();encryptSingleObject(item);}} else {encryptSingleObject(object);}}private static void encryptSingleObject(Object object) throws Throwable {if (object != null) {String className = object.getClass().getName();List<Field> EncryptTagFields = ENCRYPT_TAG_FIELDS.get(className);if (EncryptTagFields == null) {EncryptTagFields = findEncryptTagFields(object);ENCRYPT_TAG_FIELDS.putIfAbsent(className, EncryptTagFields);}encryptFields(object, EncryptTagFields);}}private static void encryptFields(Object object, List<Field> EncryptTagFields) throws Throwable {if (object != null && !EncryptTagFields.isEmpty()) {String[] originalValues = new String[EncryptTagFields.size()];for(int i = 0; i < EncryptTagFields.size(); ++i) {Field field = (Field)EncryptTagFields.get(i);String value = (String)field.get(object);originalValues[i] = value;}for(int i = 0; i < EncryptTagFields.size(); ++i) {Field field = (Field)EncryptTagFields.get(i);String value = originalValues[i];if (value == null) {continue;}Field encryptField = getEncryptField(object, field);if (encryptField == null) {continue;}String encryptValue = encryptFieldValue(value);encryptField.set(object, encryptValue);field.set(object, null);}}}private static String encryptFieldValue(String value) {String encryptValue = value + "encrypt";return encryptValue;}public static void decrypt(Object object) throws Throwable {if (object == null) {return;}String className = object.getClass().getName();List<Field> encryptTagFields = ENCRYPT_TAG_FIELDS.get(className);if (encryptTagFields == null) {encryptTagFields = findEncryptTagFields(object);ENCRYPT_TAG_FIELDS.putIfAbsent(className, encryptTagFields);}decryptFields(object, encryptTagFields);}private static void decryptFields(Object object, List<Field> encryptTagFields) throws Throwable {if (encryptTagFields.isEmpty()) {return;}for (int i = 0; i < encryptTagFields.size(); ++i) {Field field = encryptTagFields.get(i);Field encryptField = getEncryptField(object, field);Object fieldValue = encryptField.get(object);if (fieldValue == null) {continue;}if (fieldValue instanceof String) {String value = (String) fieldValue;value = AesUtil.decrypt(value);field.set(object, value);encryptField.set(object, null);}}}private static List<Field> findEncryptTagFields(Object object) {Class clazz = object.getClass();List<Field> fieldList = new ArrayList<>();for(; clazz != null; clazz = clazz.getSuperclass()) {Field[] declaredFields = clazz.getDeclaredFields();int length = declaredFields.length;for(int index = 0; index < length; ++index) {Field field = declaredFields[index];if (field.getAnnotation(EncryptTag.class) != null) {if (field.getType() == String.class) {field.setAccessible(true);fieldList.add(field);} else {LOGGER.error("@EncryptTag should be used on String field. class: {}, fieldName: {}", clazz.getName(), field.getName());}}}}return fieldList;}private static Field getEncryptField(Object object, Field field) throws Exception {String encryptFieldName = AesUtil.encrypt(field.getName());Field encyptField = getField(object, encryptFieldName);if (encyptField == null) {throw new Exception(object.getClass() + "对象没有对应的加密字段:" + encryptFieldName);} else {encyptField.setAccessible(true);return encyptField;}}public static Field getField(Object object, String fieldName) {for(Class clazz = object.getClass(); clazz != null; clazz = clazz.getSuperclass()) {Field[] var3 = clazz.getDeclaredFields();int var4 = var3.length;for(int var5 = 0; var5 < var4; ++var5) {Field field = var3[var5];if (field.getName().equals(fieldName)) {return field;}}}return null;}
}

三、日志脱敏方案

  日志脱敏,核心在于序列化时对于敏感字段修改其序列化方式。各大序列化工具一般都有序列化自定义功能,本文以fastjson为例讲解实现,实现方式有两种:

  1. 基于注解@JSONField实现:不建议使用,对业务入侵太大。
  2. 基于序列化过滤器:建议使用,fastjson提供了多种SerializeFilter:
  • PropertyPreFilter:根据PropertyName判断是否序列化
  • PropertyFilter:根据PropertyName和PropertyValue来判断是否序列化
  • NameFilter:修改Key,如果需要修改Key,process返回值则可
  • ValueFilter:修改Value
  • BeforeFilter:序列化时在最前添加内容
  • AfterFilter:序列化时在最后添加内容

通过实现ValueFilter自定义序列化扩展,针对目标类以及字段进行脱敏返回。

核心代码简化如下:

public class FastjsonValueFilter implements ValueFilter {@Overridepublic Object process(Object object, String name, Object value) {if (needDesensitize(object, name)) {return desensitize(value);}}
}
String s = JSON.toJSONString(new Person("131xxxx1552","123@163.com"),new FastjsonValueFilter());

在标记脱敏字段以及对应方法时,可以通过配置的方法, 对类相关的脱敏字段以及方法进行封装。

要求不高的话添加响应的注解也可实现。

四、输出脱敏

  在输出层织入切面进行拦截,在切面内实现脱敏逻辑。实现逻辑跟日志脱敏类似,需要对脱敏字段进行标记以及对应脱敏方法。

如果是Spring Boot集成,配置 Spring MVC 的话只需继承 WebMvcConfigurer 覆写 configureMessageConverters方法,支持全局和指定类脱敏配置,示例如下::

@Configuration
public class FastJsonWebSerializationConfiguration implements WebMvcConfigurer {@Bean(name = "httpMessageConverters")public HttpMessageConverters fastJsonHttpMessageConverters() {// 1.定义一个converters转换消息的对象FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();// 2.添加fastjson的配置信息,比如: 是否需要格式化返回的json数据FastJsonConfig fastJsonConfig = new FastJsonConfig();fastJsonConfig.setSerializerFeatures(SerializerFeature.PrettyFormat);// 中文乱码解决方案List<MediaType> mediaTypes = new ArrayList<>();//设定json格式且编码为UTF-8mediaTypes.add(MediaType.APPLICATION_JSON_UTF8);fastConverter.setSupportedMediaTypes(mediaTypes);//添加全局自定义脱敏fastJsonConfig.setSerializeFilters(new ValueDesensitizeFilter());//添加指定类脱敏方法Map<Class<?>, SerializeFilter> classSerializeFilters = new HashMap<>();classSerializeFilters.put(Employee.class, new FastjsonValueFilter());fastJsonConfig.setClassSerializeFilters(classSerializeFilters);// 3.在converter中添加配置信息fastConverter.setFastJsonConfig(fastJsonConfig);// 4.将converter赋值给HttpMessageConverterHttpMessageConverter<?> converter = fastConverter;// 5.返回HttpMessageConverters对象return new HttpMessageConverters(converter);}
}

本文的引用仅限自我学习如有侵权,请联系作者删除。
参考知识
详解企业级数据脱敏方案


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/890269.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

任务2 配置防火墙firewalld

基本概念 概述 支持动态更新防火墙规则 不重启即可创建、修改和删除规则 使用区域和服务来简化防火墙配置 区域 一组预定义的规则&#xff0c;防火墙策略集合&#xff08;或策略模板&#xff09; 把网络分配到不同的区域中&#xff0c;并为网络及其关联的网络接口或流量源…

HIPT论文阅读

题目《Scaling Vision Transformers to Gigapixel Images via Hierarchical Self-Supervised Learning》 论文地址&#xff1a;[2206.02647] Scaling Vision Transformers to Gigapixel Images via Hierarchical Self-Supervised Learning 项目地址&#xff1a;mahmoodlab/HI…

重拾设计模式--状态模式

文章目录 状态模式&#xff08;State Pattern&#xff09;概述状态模式UML图作用&#xff1a;状态模式的结构环境&#xff08;Context&#xff09;类&#xff1a;抽象状态&#xff08;State&#xff09;类&#xff1a;具体状态&#xff08;Concrete State&#xff09;类&#x…

[WiFi] WiFi 802.1x介绍及EAP认证流程整理

802.1X Wi-Fi 802.1X 是一种网络访问控制协议&#xff0c;常用于保护无线网络。它提供了一种基于端口的网络访问控制机制&#xff0c;主要用于在用户和网络之间建立安全的连接。以下是 802.1X 的一些关键特点&#xff1a; 认证框架 802.1X 使用 EAP&#xff08;可扩展认证协议…

服务器数据恢复—V7000存储中多块磁盘出现故障导致业务中断的数据恢复案例

服务器存储数据恢复环境&#xff1a; 一台V7000存储上共12块SAS机械硬盘&#xff08;其中1块是热备盘&#xff09;&#xff0c;组建了2组Mdisk&#xff0c;创建了一个pool。挂载在小型机上作为逻辑盘使用&#xff0c;小型机上安装的AIXSybase。 服务器存储故障&#xff1a; V7…

网络安全防范

实践内容 学习总结 PDR&#xff0c;$$P^2$$DR安全模型。 防火墙&#xff08;Firewall&#xff09;&#xff1a; 网络访问控制机制&#xff0c;布置在网际间通信的唯一通道上。 不足&#xff1a;无法防护内部威胁&#xff0c;无法阻止非网络传播形式的病毒&#xff0c;安全策略…

你的第一个博客-第一弹

使用 Flask 开发博客 Flask 是一个轻量级的 Web 框架&#xff0c;适合小型应用和学习项目。我们将通过 Flask 开发一个简单的博客系统&#xff0c;支持用户注册、登录、发布文章等功能。 步骤&#xff1a; 安装 Flask 和其他必要库&#xff1a; 在开发博客之前&#xff0c;首…

LLaMA-Factory(一)环境配置及包下载

LLaMA-Factory(一&#xff09;环境配置及包下载 本机配置1. git下载2.创建虚拟环境3. 下载官方包内依赖4. 下载bitsandbytes5. 启动项目6. 可能出现问题1&#xff1a;pip install 出现 error: subprocess-exited-with-error 错误7. 可能出现问题2&#xff1a; ModuleNotFoundEr…

clickhouse-题库

1、clickhouse介绍以及架构 clickhouse一个分布式列式存储数据库&#xff0c;主要用于在线分析查询 2、列式存储和行式存储有什么区别&#xff1f; 行式存储&#xff1a; 1&#xff09;、数据是按行存储的 2&#xff09;、没有建立索引的查询消耗很大的IO 3&#xff09;、建…

计算机网络:运输层 —— TCP 的选择确认(SACK)

文章目录 TCP 的选择确认协商与启用工作机制接收方发送方 TCP 的选择确认 在 TCP 传输过程中&#xff0c;由于网络拥塞、链路故障等因素&#xff0c;数据可能会出现丢失或乱序的情况。传统的 TCP 确认机制是累积确认&#xff0c;TCP 接收方只能对按序收到的数据中的最高序号给…

HTML语法规范

HTML语法规则 HTML 标签是由尖括号包围的关键词&#xff0c;标签通常是成对出现的&#xff0c;例如 <html> 和 </html>&#xff0c;称为双标签 。标签对中的第一个标签是开始标签&#xff0c;第二个标签是结束标签单标签比较少&#xff0c;例如<br />&#x…

STL 剖析

STL 六大组件 「STL 六大组件的交互关系」 Container 通过 Allocator 取得数据储存空间Algorithm 通过 Iterator 存取 Container 内容Functor 可以协助 Algorithm 完成不同的策略变化Adapter 可以修饰或套接 Functor、Iterator 配置器(allocator) 配置器&#xff1a;负责空间…

Y3编辑器教程8:资源管理器与存档、防作弊设置

文章目录 一、资源管理器简介1.1 界面介绍1.2 资源商店1.3 AI专区1.3.1 AI文生图1.3.2 AI图生图1.3.3 立绘头像 二、导入导出2.1 文件格式2.2 模型导入2.2.1 模型制作后导出2.2.2 模型文件导入Y3编辑器2.2.3 Y3编辑器角色、装饰物模型要求 2.3 纹理导入2.4 材质贴图2.4.1 材质支…

DL作业11 LSTM

习题6-4 推导LSTM网络中参数的梯度&#xff0c; 并分析其避免梯度消失的效果 LSTM&#xff08;长短期记忆网络&#xff09;是一种特殊的循环神经网络&#xff08;RNN&#xff09;&#xff0c;旨在解决普通 RNN 在处理长序列时遇到的梯度消失和梯度爆炸问题。它通过设计多个门…

面试题整理9----谈谈对k8s的理解1

谈谈对k8s的理解 1. Kubernetes 概念 1.1 Kubernetes是什么 Kubernetes 是一个可移植、可扩展的开源平台&#xff0c;用于管理容器化的工作负载和服务&#xff0c;方便进行声明式配置和自动化。Kubernetes 拥有一个庞大且快速增长的生态系统&#xff0c;其服务、支持和工具的…

解决MySQL安装难题:vcruntime140_1.dll文件丢失修复指南

在安装MySQL的过程中&#xff0c;用户可能会遇到一个常见的问题&#xff1a;“找不到vcruntime140_1.dll&#xff0c;无法继续执行代码”。这个错误提示表明系统缺少一个关键的动态链接库文件&#xff0c;这对于运行依赖于它的应用程序至关重要。本文将详细介绍vcruntime140_1.…

【前后端】HTTP网络传输协议

近期更新完毕&#xff0c;建议关注、收藏&#xff01; http请求 URL 严格意义上应该是URI http or https http不加密不安全&#xff1b;https加密协议&#xff08;公网使用&#xff09; http端口号80 https端口号443GET or POST GET和POST是HTTP请求的两种基本方法. 因为POST需…

多线程 - 自旋锁

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 多线程 - 自旋锁 收录于专栏[Linux学习] 本专栏旨在分享学习Linux的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 概述 原理 优点与…

thinkphp5验证码captcha无法显示

排查思路 是否开启gd2以及gd2排查bom排查代码清除缓存 开启gd/gd2 找到php.ini 开启dg2库 去掉前面的;注释&#xff0c;有的可能会带.dll后缀影响不大 然后通过生成图片验证是否成功 查看是否存在bom 修改为utf-8即可&#xff0c;如果你的代码携带bom也需要排查一下 代码问…

Flutter组件————FloatingActionButton

FloatingActionButton 是Flutter中的一个组件&#xff0c;通常用于显示一个圆形的按钮&#xff0c;它悬浮在内容之上&#xff0c;旨在吸引用户的注意力&#xff0c;并代表屏幕上的主要动作。这种按钮是Material Design的一部分&#xff0c;通常放置在页面的右下角&#xff0c;但…