安全测评之安全功能整改设计及代码
文章目录
- 安全功能
- 审计模块参考
- 审计模块实现
- 使用TypeHandler实现数据的加解密转换
安全功能
安全审计要求:系统应提供覆盖所有用户的安全审计功能,对系统重要安全事件(包括用户和权限的增删改、配置定制、审计日志维护、用户登录和退出、越权访问、连接超时、密码重置、数据的备份和恢复等系统级事件,及业务数据增删改、业务流程定制、交易操作中断等业务级事件)进行审计
1.审计数据产生:
要求:系统审计记录表 日期、时间、事件类型、用户身份(用户名和IP地址,且应具有唯一性标识)、事件描述和事件结果
2.审计查阅:
要求:系统应提供对审计数据进行查询、排序、分类、分析统计的功能,按照行为主体、时间、事件类型等属性
3.异常事件告警:
要求:系统应对异常事件根据严重程度进行等级划分,当异常事件发生时依据安全策略采用弹出告警窗、声光报警、短信通知、邮件通知等方式进行告警;
异常事件包括连续登录失败、越权访问、IP地址异常等
4.审计事件存储:
i 要求:系统应提供对审计数据进行手动或自动备份的功能,自动备份需记录审计日志
ii 要求:系统应保证6个月内的审计记录无法被修改、删除和覆盖,6个月或更早之前的审计记录可依据安全策略进行覆盖
iii 要求:系统应可设置审计记录存储的容量上限,至少为1G,并在容量即将达到上限时应进行告警,弹出告警窗、声光报警、短信通知、邮件通知等方式
审计模块参考
参考:
https://www.cnblogs.com/samlin/archive/2010/02/08/log-operation-management.html
https://blog.csdn.net/t_jindao/article/details/85259145
https://blog.csdn.net/he90227/article/details/44175365
https://www.cnblogs.com/hooray/archive/2012/09/05/2672133.html
系统操作日志设计:
我们在做企业管理系统时,有多多少少都有对数据的完整性有所要求,比如要求系统不能物理删除记录,要求添加每一条数据时都要有系统记录、或者更新某条数据都需要跟踪到变化的内容、或者删除数据时需要记录谁删除了,何时删除了,以便误删后可以通过系统的XXX功能来恢复误删的数据。
为什么要做操作日志?
其实上文也描述了一些,其主要目的就是跟踪到每一个用户在系统的操作行为,如对数据进行查询、新增、编辑或删除甚至是登录等行为。更进一步的理解可以说是对用户使用系统情况的跟踪,对数据的跟踪防止数据意外删除、更改时有所记录,有所依据,以便对数据的还原,从某种程序上可以保护数据的完整性。
3.异步插入数据库日志记录
https://blog.csdn.net/hightrees/article/details/78765580
4.系统错误日志实现
https://blog.csdn.net/weixin_42456466/article/details/89672890
获取客户端ip地址
https://blog.csdn.net/u011521890/article/details/74990338
审计模块实现
- 自定义注解标记在handler方法上
@OperationLogger(name = "用户登录", table = OperationTable.USER, operationType = OperationType.SPEC)@OperationLogger(name = "退出登录", table = OperationTable.USER, operationType = OperationType.SPEC)@OperationLogger(name = "添加用户", table = OperationTable.USER, operationType = OperationType.ADD)@OperationLogger(name = "修改密码", table = OperationTable.USER, operationType = OperationType.UPDATE,idRef = 0)@OperationLogger(name = "批量添加用户", table = OperationTable.USER, operationType = OperationType.UPDATE) @OperationLogger(name = "编辑用户信息", table = OperationTable.USER, operationType = OperationType.UPDATE,idRef = 0)@OperationLogger(name = "删除用户", table = OperationTable.USER, operationType = OperationType.DELETE,idRef = 0)@OperationLogger(name = "用户注册", table = OperationTable.USER, operationType = OperationType.SPEC)@OperationLogger(name = "忘记密码", table = OperationTable.USER, operationType = OperationType.SPEC)
@RestController
@RequestMapping("/user")
public class UserController{@Anonymous@RequestMapping("login.do")@OperationLogger(name = "用户登录", table = OperationTable.USER, operationType = OperationType.SPEC)public ResponseData login(){ResponseData responseData = new ResponseData<>();try {// todo 登录操作;if(/**登录成功*/){responseData.setStatus(ServiceResponseCodeEnum.SYS_SUCCESS.getCode());responseData.setMsg(ServiceResponseCodeEnum.SYS_SUCCESS.getMsg()); }else{responseData.setStatus(ServiceResponseCodeEnum.SYS_FAILED.getCode());responseData.setMsg(ServiceResponseCodeEnum.SYS_FAILED.getMsg());}}catch (Exception e) {responseData.setStatus(ServiceResponseCodeEnum.SYS_FAILED.getCode());responseData.setMsg(e.getMessage());}return responseData;}
}
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperationLogger {/** 操作业务名 */String name();/** 操作表名 */OperationTable table();/** 操作人 id */long operatorId() default 0L;/** 操作人名称 */String operatorName() default "";/** 不需要记录的字段 */String[] column() default {};/** 操作类型 操作类型(添加ADD,删除DELETE,修改UPDATE)*/OperationType operationType();String desc() default "";/** 0业务日志;1系统日志 */byte type() default 0;/** 异常事件严重程度 */byte level() default 1;/** 操作业务id */int idRef() default -1;/** 成功不记录,只记录失败操作*/boolean toLogger() default false;
}
public enum OperationType {SPEC((byte)0, "SPEC"),ADD((byte)1, "ADD"),DELETE((byte)2, "DELETE"),UPDATE((byte)3, "UPDATE"),private final Byte code;private final String type;OperationType(Byte code, String type) {this.code=code;this.type=type;}public Byte getCode() {return code;}public String getType() {return type;}
}
public enum OperationTable {USER(1L, "user"),DEVICE(2L, "device");private final Long id;private final String name;OperationTable(Long id, String name) {this.id=id;this.name=name;}public Long getId() {return id;}public String getName() {return name;}
}
- 自定义注解处理器
@Aspect
@Component
public class OperationLogAop {Logger logger = LoggerFactory.getLogger(OperationLogAop.class);@AutowiredILoggerService loggerService;//Controller层切点@Pointcut("@annotation(com.xyz.log.OperationLogger)")public void controllerAspect() {}@Around("controllerAspect()")public Object doAround(ProceedingJoinPoint joinPoint) {ResponseData result = null;try {// 记录操作日志...谁..在什么时间..做了什么事情..result = (ResponseData) joinPoint.proceed();// 在操作失败时候也记录操作人idLong id = 0L;if(result != null){if(result.getData() instanceof UserDetailVo){id = ((UserDetailVo) result.getData()).getId();} else if(result.getData() instanceof UserServiceBean){id = ((UserServiceBean) result.getData()).getId();}handleLog(joinPoint, result, id);}} catch (Throwable e) {// todo// 异常处理记录日志..log.error(e);}return result;}private void handleLog(JoinPoint joinPoint,ResponseData result,Long operatorId) {Map<String,Object> nameAndArgs = null;Long operationKey = null;String status = result.getStatus();try {HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String ip = IpUtil.getIpAddr(request);//获取横切的方法名String methodName = joinPoint.getSignature().getName();//获取拦截的classString classType = joinPoint.getTarget().getClass().getName();//加载这个类Class<?> clazz = Class.forName(classType);//获取这个类上的方法名Method[] methods = clazz.getDeclaredMethods();// token中放了uid,在拦截器校验token时候setsetAttribute("uid", token中uid)String uid = (String) request.getAttribute("uid");uid = uid == null? "0":uid;for (Method method:methods){// 这个方法上面的注解是否含有自定义的注解// 并且方法名等于切点访问的方法名if (method.isAnnotationPresent(OperationLogger.class)&&method.getName().equals(methodName)){//获取method的注解OperationLogger operationLogger = method.getAnnotation(OperationLogger.class);HelmetOperationLogDto logDto = new HelmetOperationLogDto();//获取用户请求方法的参数并序列化为JSON格式字符串Object[] args = joinPoint.getArgs();if(null != args && args.length > 0) {//获取参数名称和值nameAndArgs = getParameMap(joinPoint);int idRef = operationLogger.idRef();if(idRef != -1){operationKey = (Long) args[idRef];}if(operationLogger.column().length != 0){for (String s : operationLogger.column()) {nameAndArgs.remove(s);}}}logDto.setName(operationLogger.name());logDto.setTableId(operationLogger.table().getId());logDto.setTableName(operationLogger.table().getName());logDto.setOperatorId(Long.valueOf(uid).equals(0L)?operatorId:Long.valueOf(uid));logDto.setOperationType(operationLogger.operationType().getCode());logDto.setOperationTime(new Date());logDto.setOperationIp(ip);logDto.setType(operationLogger.type());logDto.setNameAndArgs(nameAndArgs);logDto.setOperationKey(operationKey);logDto.setLevel(operationLogger.level());if(ServiceResponseCodeEnum.SYS_SUCCESS.getCode().equals(status)){logDto.setOperationDesc("操作成功");} else if(ServiceResponseCodeEnum.SYS_FAILED.getCode().equals(status)){logDto.setOperationDesc("操作失败");logDto.setLevel((byte)2);result.setData(null);} else if(ServiceResponseCodeEnum.PERMISSION_NOT_RIGHT.getCode().equals(status)){logDto.setOperationDesc("越权访问");logDto.setLevel((byte)3);result.setData(null);} else if(ServiceResponseCodeEnum.FAILED_LOGIN.getCode().equals(status)){logDto.setOperationDesc("登录失败");result.setData(null);result.setStatus(ServiceResponseCodeEnum.SYS_FAILED.getCode());} else if(ServiceResponseCodeEnum.FAILED_LOGIN_REPEATED.getCode().equals(status)){logDto.setOperationDesc("连续登录多次失败");logDto.setLevel((byte)3);result.setData(null);result.setStatus(ServiceResponseCodeEnum.SYS_FAILED.getCode());} else if(ServiceResponseCodeEnum.FAILED_LOGIN_LOCK.getCode().equals(status)){logDto.setOperationDesc("连续登录多次失败, 账号已经锁定");logDto.setLevel((byte)3);result.setData(null);result.setStatus(ServiceResponseCodeEnum.SYS_FAILED.getCode());}loggerService.addLog(logDto);}}} catch (Exception e) {logger.error("记录用户操作日志失败!", e);}}public Map<String, Object> getParameMap(JoinPoint joinPoint) {Map<String, Object> map = new HashMap<String, Object>();Object[] args = joinPoint.getArgs(); // 参数值String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames(); // 参数名for (int i = 0; i < argNames.length; i++) {if ("response".equals(argNames[i]) || "request".equals(argNames[i]) || "password".equals(argNames[i])) {continue;} else {map.put(argNames[i], args[i]);}}return map;}}
使用TypeHandler实现数据的加解密转换
https://www.cnblogs.com/wangjuns8/p/8688815.html
- 先自定义一个转化类,继承自BaseTypeHandler
public class AESEncryptHandler extends BaseTypeHandler{@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType) throws SQLException {ps.setString(i, AESUtil.encrypt((String)parameter));}@Overridepublic Object getNullableResult(ResultSet rs, String columnName) throws SQLException {String columnValue = rs.getString(columnName);return AESUtil.decrypt(columnValue);}@Overridepublic Object getNullableResult(ResultSet rs, int columnIndex) throws SQLException {String columnValue = rs.getString(columnIndex);return AESUtil.decrypt(columnValue);}@Overridepublic Object getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {String columnValue = cs.getString(columnIndex);return AESUtil.decrypt(columnValue);}
}
2)在resultMap上加引用,对应从数据库里取数据时转换成JAVA对象时的解密。
–> 在mapper.xml里的对应字段上加上引用
<arg column="code" jdbcType="VARCHAR" javaType="java.lang.String" typeHandler="com.xyz.handler.AESEncryptHandler"/>
<arg column="user_phone" jdbcType="VARCHAR" javaType="java.lang.String" typeHandler="com.xyz.handler.AESEncryptHandler"/>
<resultMap id="UserByTaskResultMap" type="com.xyz.dto.user.UserByIdDto"><result property="id" column="id"/><result property="code" column="code" typeHandler="com.xyz.handler.AESEncryptHandler"/><result property="username" column="username"/><result property="num" column="num"/>
</resultMap>
所有涉及user表的code,user_phone字段都在这个mapper的resultMap映射关系中加上typeHandler,进行解密
3)在sql语句中加入引用,对应从JAVA对数据库传递数据的加密动作(所有用到敏感信息的地方都要加)
insert和update语句
<if test="code != null" >
#{code,jdbcType=VARCHAR,typeHandler=com.xyz.handler.AESEncryptHandler},
</if>
<if test="userPhone != null" >
#{userPhone,jdbcType=VARCHAR,typeHandler=com.xyz.handler.AESEncryptHandler},
</if>
安全测评之安全功能整改设计及代码相关推荐
- 安全测评之渗透测试整改设计和代码
文章目录 安全测评之渗透测试 1. 任意文件上传漏洞 2. 中间件版本泄漏 3. 目录遍历 4. 关键会话重放攻击 5. 检测存在风险的无关服务和端口 6. 不安全的HTTP方法 安全测评之渗透测试 ...
- App中 微信分享 代付功能 业务设计 与 代码实现
先上图 场景 本例的代付场景是,开发一款APP应用,APP中具有支付能力,按照微信 支付对接要求已经完成APP支付的对接.现在要玩点新花样,找人代付订 单.用户在APP中下单后选择找人代付,将分享H5 ...
- 学生综合测评系统(c++课程设计)
目录 <实验课题> <系统分析与设计> <代码实现> (1) 类的编写 (2) 链表的使用 (3) 菜单目录的实现 <程序演示> <源码+开题报告 ...
- 功能测试用例设计方法有哪些?
知识永远学不完,但多懂一点知识就会让生活更轻松一点! 一.什么是功能测试? ①顾名思义,功能测试就是对产品的各功能进行验证,根据功能测试用例,逐项测试,检查产品是否达到需求要求的功能. ②功能测试也叫 ...
- Atitit截屏功能的设计解决方案
Atitit截屏功能的设计解决方案 自己实现.... 使用快捷键.. 弹出自己的win,,背景是屏幕快照 点击鼠标光标变成十字状态 出现截屏窗口 调整截屏窗口位置与大小 释放鼠标,三个btn,, 复 ...
- ASP.NET MVC:缓存功能的设计及问题
ASP.NET MVC:缓存功能的设计及问题 这是非常详尽的asp.net mvc中的outputcache 的使用文章. [原文:陈希章 http://www.cnblogs.com/chenxiz ...
- axios取消功能的设计与实现
取消功能的设计与实现 #需求分析 有些场景下,我们希望能主动取消请求,比如常见的搜索框案例,在用户输入过程中,搜索框的内容也在不断变化,正常情况每次变化我们都应该向服务端发送一次请求.但是当用户输入过 ...
- Java充电宝模型设计_继续探讨点赞功能模块设计
继续探讨点赞功能模块设计 前几天我们设计了点赞模块的模块,大致思路就是: 用户点赞,首先缓存到redis中进行保存,redis中既要保存点赞总数,还要保存点赞记录.然后定时执行redis数据到数据库中 ...
- 界面设计方法(2)— 6.功能按钮设计(删除,保存,提交)
接着上一篇功能按钮设计的说明,这一篇介绍功能按钮"删除.保存.提交"的三种形式.这三种形式是界面内容处理过程中的功能(删除.保存).界面处理完成时的功能(提交).同样分为基本功能和 ...
最新文章
- 【机器学习实战 第九章】树回归 CART算法的原理与实现 - python3
- http提交json格式数据自动加\
- ICA(独立成分分析)笔记
- 使用pip安装python库的几种方式,解决pip安装python库慢的问题
- 微信卡券 - 微信公众平台 整理笔记
- 小话设计模式四:策略模式
- 1499元!魅族Watch“天青”配色正式首销:与手机完全互联互通
- Spring实战基础1
- 2021数学建模B题详细思路
- Java基础每日一练—第5天:预测身高案列
- 使用nim master为oracle2分区安装os,使用 NIM 资源部署自定义的 AIX 系统
- qua数据统计缺失问题之终结
- 细致的网站开发流程有哪些呢?
- 手机照片误删怎么找回
- NR5G基础概念扫盲
- 反向传播神经网络(BPNN)的实现(Python,附源码及数据集)
- 关于解决移动端息屏后定时器不工作的问题
- 【调剂】浙江大学计算机学院机械专业2023年硕士研究生招生调剂通知
- Unity3D制作3D虚拟漫游场景(一)
- 基于机器学习技术的用户行为分析:当前模型和应用研究综述(A survey for user behavior analysis based on machine learning technique)