三级等保测评,如何通过
引言
你好!本文旨在帮助想要过三级等保测评的项目一些指引,尽量避免在整改过程中走弯路和碰壁。
以及提醒开发者,项目中的哪些东西需要尽早完成,避免在项目成型后再去改变架构从而影响到项目的稳定性、可维护性等等。就算你本身并不是网络安全工程师(本人是个普通的JAVA后端工程师),业务服务器整改以JAVA为例子,但就算你的项目并非JAVA作为服务器,本文提供的解决方案依旧通用,只是具体实现需要你自行完成,根据本篇文章的指引,也能完成三级等保测评。
PS:如果你的项目已经成型并上线。那在你整改时,有改变项目架构的风险,整改时请谨慎一些。项目也需要一个整体性的测试才去迭代上线。
所需准备
1.堡垒机
2.服务器(单个或多个)
3.客户端(单个或多个)
4.测评机构(一定要有认证的机构)
5.安全文档(保密协议,服务器授权书,管理流程等等文档)
堡垒机 :堡垒机作为登录服务器的入口,做了很多安全性的架构,很多等保测评机构也会直接点名需要堡垒机。
若你项目没有足够的人员维护服务器的安全性。那么购买堡垒机(云服务器的大厂商基本都有堡垒机,自行搜索)是个不错的解决方案,他能给你的服务器解决很多配置问题,无需你对服务器的安全性过多操心。
PS:如果你觉得可以搞定服务器(硬件)的安全性问题,可以抵御大部份的攻击,也可以选择不买堡垒机。
服务器 :
1.硬件服务器:这里只是指你的硬件服务器。如果有多个也需要一起整改。此类整改基本为固定的操作,Linux系统需要修改一些配置,对你项目的软件服务器影响较小,也比较简单,会在后面做说明。
2.业务服务器:这里是指你的业务服务器,包括数据库,一般都会有对外部的接口,所以需要解决一些非法访问,敏感数据传输,越权问题,数据安全问题等等。
客户端 :这里只是指你的项目是否有对外的客户端,有则一起整改。此类整改基于你的项目架构。由于客户端基本对外开放,所以敏感数据加密,传输加密,防暴力破解等为基础且必要的整改。
测评机构 :测评机构的收费不一,可根据自己情况选择,但一定要找到有政府正规认证的测评机构,根据他们的整改建议一点点修改,可以保证你一次通过。
当然,理论上也可以不请测评机构,如果你们项目组中有人有类似经验的人,或者有厉害的网安工程师给与你们整改建议,也可以自己整改。但有一点是,正式的测评一年只有一次机会,一次不通过需要等到第二年。所以对于没有过类似工作经验的,找测评机构会少走很多弯路。
安全文档 :一些需要规范化的文档,可能需要专人整理文档,这是一个必要且繁杂的工作,如果你请了测评机构。则他们会为你提供文档模板及文案。这也是他们需要帮你审核整改的内容之一。
所有文档都需要打印成纸质并盖公章,需要和你项目备案时的公章一样
服务器整改(硬件部分)
1.添加三员,且三权必须分离
服务器管理员,安全管理员,审计管理员。也就是至少添加这三个账号,需要不同的权限(即三权分离)。其他账号根据自己的业务而定。包括数据库也需要添加三员。
2.密码复杂度
(1)密码90天更新一次,密码改为90天更改一次
//将9999改成90
vim /etc/login.defs
(2)系统的登录密码需要配置密码复杂度(密码最小长度8位,其中包括2个大写字母、1个小写字母、4个数字、1个特殊字符;等保要求是最少8位字符,其中包括字母、数字、特殊字符)
//配置如图红框内
vim /etc/pam.d/system-auth
3.登录失败、超时处理
(1)登录失败3次锁定账户5分钟,时间越久越好。登录超时5分钟自动退出。根据你的具体情况而定
//配置如图红框内
vim /etc/pam.d/system-auth
(2)登录超时5分钟(按秒计算)自动退出,根据业务情况设置
//配置如图红框内
vim /etc/profile
4.日志转存至日志审计
服务器日志需要转存至日志审计设备中。若有堡垒机或者其他处理方式,可以不用此配置。只需要做一个异地(如服务器在贵阳,可以在上海的云服务器中储存日志)的日志增量备份和全量备份。
//服务器日志转存至日志审计设备中,如果你买了obs桶可以不配置此项
vim /etc/rsystem.conf
5.日志需要本机储存:
至少6个月
//将图中红框内weekly改成monthlyrotate 4 改成 rotate 6
vim /etc/logrotate.conf
6.限制IP登录:
根据具体情况,最好可以限制到某些IP段才能登录服务器。如公司wifi的IP段等。
7.杀毒软件安装:
建议最好为开源软件,linux推荐ClamAV 。
服务器整改(业务服务器)
1.数据库整改(以我的项目为例springboot+mybatis+pgsql)
(1)添加三员:同硬件服务器,数据库服务器也需要创建3个账号,并且分配不同权限(权限主要用于区分不同,可随意分配)
2.加密
注:如果你的项目中有MD5加密的方式,请换成需要密钥的对称加密,或非对称加密,或对MD5串进行二次加密
(1)敏感信息加密存储:如手机号,身份证等敏感信息,需要加密存入数据库中(PS:mybatis拦截器)
本项目是基于Interceptor(自定义拦截器)来实现拦截加密。Java与数据库的数据交换基本都是基于实体类。所以用通过注解类标识需要加密字段的方式,进行字段加密处理。
先创建用于注解实体类的注解类。
//创建注解类,用于注解整个实体类。标识需要处理的类
import java.lang.annotation.*;
/**
* 注解敏感信息类的注解
* @author LeeMJ
* @date 2021-9-3
*/
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {
//可不写默认值
}
有了注解实体类的注解后,再创建用于注解字段的注解类。
//创建注解类,用于注解需要处理的字段。
import java.lang.annotation.*;
/**
* 注解敏感信息类的注解
* @author LeeMJ
* @date 2021-9-3
*/
@Inherited
@Target({ ElementType.TYPE })
@Retention(RetentionPolicy.RUNTIME)
public @interface SensitiveData {
//可不写默认值
}
(参考)实体类注解例子
//创建注解类,用于注解需要处理的字段。
@SensitiveData
public class UserInfo {
//个人账户信息表
@SensitiveField
private String phone; //手机号
@SensitiveField
private String idcard; //身份证
private String user_nickname; //用户昵称
}
接下来就是关键的 (加密)拦截器 部分,拦截查找是否有注解的加密字段。并进行加密操作存入数据库。
import com.fecred.bisouiya.entity.bo.SensitiveData;
import com.fecred.bisouiya.utils.AesUtils;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.PreparedStatement;
import java.util.Objects;
import java.util.Properties;
/**
* 数据库敏感信息加密
* @author LeeMJ
* @date 2021-9-3
*/
@Component
//拦截 ParameterHandler 的 setParameters 方法 动态设置参数
@Intercepts({
@Signature(type = ParameterHandler.class,method = "setParameters",args = PreparedStatement.class),
})
@ConditionalOnProperty(value = "domain.encrypt", havingValue = "true")
public class EncryptInterceptor implements Interceptor {
private static Logger log= LoggerFactory.getLogger(EncryptInterceptor.class);
@Override
public Object intercept(Invocation invocation) throws Throwable {
log.info("数据加密拦截器EncryptInterceptor");
if (invocation.getTarget() instanceof ParameterHandler) {
ParameterHandler parameterHandler = (ParameterHandler) invocation.getTarget();
// 反射获取 实体类 参数对像
Field parameterField = parameterHandler.getClass().getDeclaredField("parameterObject");
parameterField.setAccessible(true);
//获取实体类
Object parameterObject = parameterField.get(parameterHandler);
if (Objects.nonNull(parameterObject)) {
Class<?> parameterObjectClass = parameterObject.getClass();
SensitiveData encryptDecryptClass = AnnotationUtils.findAnnotation(parameterObjectClass, SensitiveData.class);
//判断实体类中是否有@SensitiveData注解
if (Objects.nonNull(encryptDecryptClass)) { //实体类
Field[] declaredFields = parameterObjectClass.getDeclaredFields();
try {
for (Field field : declaredFields) {
//取出所有被EncryptDecryptField注解的字段
SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
//获取有@SensitiveField注解的元素
if (!Objects.isNull(sensitiveField)) {
field.setAccessible(true);
Object object = field.get(parameterObject);
//暂时只实现String类型的加密
if (object instanceof String) {
String value = (String) object;
//加密 这里的Encrypt我使用自定义的AES加解密工具,可用你自己的加解密方法
field.set(parameterObject, Encrypt(value));
}
}
}
} catch (Exception e) {
log.error("数据加密失败:"+e.getMessage());
}
}
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target,this);
}
@Override
public void setProperties(Properties properties) {
}
然后是 (解密)拦截器 部分,配置同上
import com.fecred.bisouiya.entity.bo.SensitiveData;
import com.fecred.bisouiya.utils.AesUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.ibatis.executor.resultset.ResultSetHandler;
import org.apache.ibatis.plugin.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Objects;
import java.util.Properties;
//拦截 ResultSetHandler 的 handleResultSets方法 动态设置返回值
@Intercepts({
@Signature(type = ResultSetHandler.class, method = "handleResultSets", args = {Statement.class})
})
@ConditionalOnProperty(value = "domain.decrypt", havingValue = "true")
@Component
public class DecryptInterceptor implements Interceptor {
private static Logger log= LoggerFactory.getLogger(EncryptInterceptor.class);
public Object intercept(Invocation invocation) throws Throwable {
log.info("拦截器DecryptInterceptor");
//获取结果集
Object result = invocation.proceed();
if (Objects.isNull(result)) {
return null;
}
if (result instanceof ArrayList) {
ArrayList resultList = (ArrayList) result;
if (CollectionUtils.isNotEmpty(resultList) && needToDecrypt(resultList.get(0))) {
for (int i = 0; i < resultList.size(); i++) {
try {
Class resultClass = result.getClass();
Field[] declaredFields = resultClass.getDeclaredFields();
for (Field field : declaredFields) {
//取出所有被EncryptDecryptField注解的字段
SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
if (!Objects.isNull(sensitiveField)) {
field.setAccessible(true);
Object object = field.get(result);
//只支持String的解密
if (object instanceof String) {
String value = (String) object;
//解密 这里的Decryp是我使用自定义的AES加解密工具,可用你自己的加解密方法
field.set(result, Decrypt(value,key));
}
}
}
} catch (Exception e) {
log.error("数据解密失败:"+e.getMessage());
}
}
}
} else {
if (needToDecrypt(result)) {
//同上相同的算法逻辑,可自行抽出方法体
try {
Class resultClass = result.getClass();
Field[] declaredFields = resultClass.getDeclaredFields();
for (Field field : declaredFields) {
//取出所有被EncryptDecryptField注解的字段
SensitiveField sensitiveField = field.getAnnotation(SensitiveField.class);
if (!Objects.isNull(sensitiveField)) {
field.setAccessible(true);
Object object = field.get(result);
//暂时只支持String的解
if (object instanceof String) {
String value = (String) object;
//同上
field.set(result, Decrypt(value));
}
}
}
} catch (Exception e) {
log.error("数据解密失败:"+e.getMessage());
}
}
}
return result;
}
/**
* 判断是否需要解密
*/
private boolean needToDecrypt(Object object) {
if(!Objects.isNull(object)){
Class<?> objectClass = object.getClass();
SensitiveData encryptDecryptClass = AnnotationUtils.findAnnotation(objectClass, SensitiveData.class);
if (Objects.nonNull(encryptDecryptClass)) {
return true;
}
return false;
}
return false;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
之后把拦截器写入配置文件中,做单独配置
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 数据库的其他配置,需要一起写入xml文件 -->
<settings>
<!-- 不区分大小写配置 -->
<setting name="mapUnderscoreToCamelCase" value="true" />
<!-- 解决数据库null字段的显示 -->
<setting name="callSettersOnNulls" value="true" />
<!-- 懒加载配置 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- true表示如果对具有懒加载特性的对象的任意调用会导致这个对象的完整加载,false表示每种属性按照需要加载 -->
<setting name="aggressiveLazyLoading" value="false" />
</settings>
<plugins>
<!-- 加密拦截器 -->
<plugin interceptor="com.fecred.bisouiya.filter.DecryptInterceptor" />
<!-- 解密拦截器 -->
<plugin interceptor="com.fecred.bisouiya.filter.EncryptInterceptor" />
</plugins>
</configuration>
再把上方的xml配置文件配置进springboot的yml配置文件中
mybatis:
# mybatis-config.xml 为上面的xml配置文件名称。建议放在yml的同目录下
config-location: classpath:mybatis-config.xml
# /mapper/*/*.xml 为我的mybatis的xml文件目录,多个用“,”隔开
mapper-locations: classpath:/mapper/*/*.xml,classpath:/mapper/*.xml
# com.xxx.xxx.entity 为我的实体类目录,可以不用包含当前包下的二级目录
type-aliases-package: com.xxx.xxx.entity
数据库加密就到这,请根据你的具体情况自行选择解决方案。!!!
4.敏感信息过滤筛选
需要再前后端交互的时候,过滤掉一些敏感字段(如手机号,身份证等敏感信息)。
如果使用 http 的请求方式传参,必须加密传输所有字段。
如果使用 https 的请求方式,需要进行敏感信息过滤。或对敏感信息进行脱敏处理或加密处理。
由于本项目在整改的时候已经成型,需要脱敏的地方较多以及进度问题,只采用了所有web端接口参数对称加密的方式进行了处理。
用 过滤器 ,进行接口过滤,修改参数进行加密处理
1.由于需要对前端传输过来的参数进行修改,需要新建HttpServletRequestWrapper的继承类来操作入参
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.Enumeration;
import java.util.Map;
public class ParameterRequestWrapper extends HttpServletRequestWrapper {
private Map<String, String[]> parameterMap; // 所有参数的Map集合
/**
* ChangeRequestWrapper构造函数
* @param request
*/
public ParameterRequestWrapper(HttpServletRequest request) {
super(request);
// 给参数集合赋值
parameterMap = request.getParameterMap();
}
/**
* 获取所有参数名
*
* @return 返回所有参数名
*/
@Override
public Enumeration<String> getParameterNames() {
Vector<String> vector = new Vector<String>(parameterMap.keySet());
return vector.elements();
}
/**
* 获取指定参数名的值,如果有重复的参数名,则返回第一个的值 接收一般变量 ,如text类型
*
* @param name 指定参数名
* @return 指定参数名的值
*/
@Override
public String getParameter(String name) {
String result = super.getParameter(name);
return result;
}
/**
* 获取指定参数名的所有值的数组,如:checkbox的所有数据
* 接收数组变量 ,如checkobx类型
*/
@Override
public String[] getParameterValues(String name) {
return parameterMap.get(name);
}
@Override
public Map<String, String[]> getParameterMap() {
return parameterMap;
}
public void setParameterMap(Map<String, String[]> parameterMap) {
this.parameterMap = parameterMap;
}
}
2.再创建一个HttpServletResponseWrapper继承类用于处理服务器的返回结果
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import java.io.*;
public class WrapperedResponse extends HttpServletResponseWrapper {
private ByteArrayOutputStream buffer = null;
private ServletOutputStream out = null;
private PrintWriter writer = null;
public WrapperedResponse(HttpServletResponse resp) throws IOException {
super(resp);
// 真正存储数据的流
buffer = new ByteArrayOutputStream();
out = new WapperedOutputStream(buffer);
writer = new PrintWriter(new OutputStreamWriter(buffer,
this.getCharacterEncoding()));
}
/** 重载父类获取outputstream的方法 */
@Override
public ServletOutputStream getOutputStream() throws IOException {
return out;
}
/** 重载父类获取writer的方法 */
@Override
public PrintWriter getWriter() throws UnsupportedEncodingException {
return writer;
}
/** 重载父类获取flushBuffer的方法 */
@Override
public void flushBuffer() throws IOException {
if (out != null) {
out.flush();
}
if (writer != null) {
writer.flush();
}
}
@Override
public void reset() {
buffer.reset();
}
/** 将out、writer中的数据强制输出到WapperedResponse的buffer里面,否则取不到数据 */
public byte[] getResponseData() throws IOException {
flushBuffer();
return buffer.toByteArray();
}
/** 内部类,对ServletOutputStream进行包装 */
private class WapperedOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bos = null;
public WapperedOutputStream(ByteArrayOutputStream stream)
throws IOException {
bos = stream;
}
@Override
public void write(int b) throws IOException {
bos.write(b);
}
@Override
public void write(byte[] b) throws IOException {
bos.write(b, 0, b.length);
}
@Override
public boolean isReady() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setWriteListener(WriteListener writeListener) {
// TODO Auto-generated method stub
}
}
}
3.加入过滤器Filter,做参数统一过滤处理
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.fecred.bisouiya.utils.AesUtils;
import com.fecred.bisouiya.utils.StringUtil;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 解密过滤器
* @author 李茂江
* @Date 2021-12-16
*/
@Order(Ordered.HIGHEST_PRECEDENCE)//控制过滤器的级别
public class DHEncryptFilter implements Filter {
private static Logger log= LoggerFactory.getLogger(TokenFilter.class);
//排除暴露链接
public List<String> dhexposeUrls=new ArrayList<>();
//过滤开关
public boolean dhisOpen=false;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
//加载配置文件的配置,只对某些借口进行操作
String tempExcludes=filterConfig.getInitParameter("dhExposeUrls");
String tempEnabled=filterConfig.getInitParameter("dhIsOpen");
if(!StringUtil.IsNullOrEmpty(tempExcludes)){
String url[]=tempExcludes.split(",");
for (int i=0;url!=null && i<url.length;i++){
dhexposeUrls.add(url[i]);
}
}
if(!StringUtil.IsNullOrEmpty(tempEnabled)){
dhisOpen=Boolean.valueOf(tempEnabled);
}
}
@SneakyThrows
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
HttpServletResponse resp = (HttpServletResponse) servletResponse;
if(!handleExposeUrls(req,resp)){
ParameterRequestWrapper parameterRequestWrapper = new ParameterRequestWrapper((HttpServletRequest)servletRequest);
//由于本项目的传参方式为form-data类型,不好做接收处理,所以统一对所有参数进行加密后,让前端把参数写入encryptParams参数下,做取值后操作
String encryptParams = parameterRequestWrapper.getParameter("encryptParams");
Map<String,String[]> m = new HashMap<String,String[]>(parameterRequestWrapper.getParameterMap());
//AesUtils.Decrypt为自定义的解密方法,请自行编写
String de= AesUtils.Decrypt(encryptParams,null);
Map paramMap = JSONObject.parseObject(de);
Iterator<String> it = paramMap.keySet().iterator();
while(it.hasNext()){
String key = it.next();
String[] IDList;
IDList=new String[1];
if(ObjectUtil.isEmpty(paramMap.get(key))){
IDList[0]="";
}else{
IDList[0]= String.valueOf(paramMap.get(key));
}
m.put(key,IDList);
}
parameterRequestWrapper.setParameterMap(m);
WrapperedResponse wrapResponse = new WrapperedResponse((HttpServletResponse) servletResponse);
filterChain.doFilter(parameterRequestWrapper,wrapResponse);
byte[] data = wrapResponse.getResponseData();
String dataa=new String(data);
log.info("原始返回数据: " + new String(data, "utf-8"));
//AesUtils.Encrypt为自定义的加密方法,请自行编写
String responseBodyMw = AesUtils.Encrypt(dataa,null);
log.info("加密返回数据: " + responseBodyMw);
writeResponse(servletResponse, responseBodyMw);
}else{
filterChain.doFilter(servletRequest,servletResponse);
}
}
@Override
public void destroy() {
}
/**
* 获取配置,判断是否需要过滤
* @param request
* @param response
* @return
*/
private boolean handleExposeUrls(HttpServletRequest request, HttpServletResponse response){
if(!dhisOpen){ return true; }
if(dhexposeUrls==null || dhexposeUrls.size()<1){ return false; }
String url=request.getServletPath();
for (String pattern:dhexposeUrls){
Pattern p=Pattern.compile("^"+pattern);
Matcher m=p.matcher(url);
if(m.find()){
return true;
}
}
return false;
}
/**
* 写入响应体
* @param response
* @param responseString
* @throws IOException
*/
private void writeResponse(ServletResponse response, String responseString)
throws IOException {
PrintWriter out = response.getWriter();
out.print(responseString);
out.flush();
out.close();
}
}
4.接着把过滤器加入配置中
import com.fecred.bisouiya.filter.DHEncryptFilter;
import com.fecred.bisouiya.filter.TokenFilter;
import com.fecred.bisouiya.filter.XssFilter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class WebConfig {
/**
* 加解密过滤
* @return
*/
@Value("${dhCheck.dhIsOpen}")
private String dhIsOpen;
@Value("${dhCheck.dhExposeUrls}")
private String dhExposeUrls;
@Value("${dhCheck.dhUrlPatterns}")
private String dhUrlPatterns;
@Bean
public FilterRegistrationBean DHEncryptFilter(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean() ;
filterRegistrationBean.setFilter(new DHEncryptFilter());
filterRegistrationBean.setName("DHEncryptFilter");
filterRegistrationBean.setOrder(1); //优先级 最高 1
//匹配链接
filterRegistrationBean.setUrlPatterns(Arrays.asList(dhUrlPatterns.split(",")));
//自定义参数,传入自定义filter
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("dhExposeUrls", dhExposeUrls);
initParameters.put("dhIsOpen", dhIsOpen);
filterRegistrationBean.setInitParameters(initParameters);
return filterRegistrationBean;
}
/**
* token过滤(自定的不用管,如果也需要,和加解密过滤器差不多的实现逻辑)
* @return
*/
@Value("${tokenCheck.isOpen}")
private String isOpen;
@Value("${tokenCheck.exposeUrls}")
private String exposeUrls;
@Value("${tokenCheck.tokenUrlPatterns}")
private String tokenUrlPatterns;
@Bean
public FilterRegistrationBean tokenFilter(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean() ;
filterRegistrationBean.setFilter(new TokenFilter());
filterRegistrationBean.setName("tokenFilter");
filterRegistrationBean.setOrder(2); //优先级 第二
//匹配链接
filterRegistrationBean.setUrlPatterns(Arrays.asList(tokenUrlPatterns.split(",")));
//自定义参数,传入自定义filter
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("exposeUrls", exposeUrls);
initParameters.put("isOpen", isOpen);
filterRegistrationBean.setInitParameters(initParameters);
return filterRegistrationBean;
}
/**
* xss攻击过滤(自定的不用管)
* @return
*/
@Value("${xss.enabled}")
private String enabled;
@Value("${xss.excludes}")
private String excludes;
@Value("${xss.urlPatterns}")
private String urlPatterns;
@Bean
public FilterRegistrationBean xssFilter(){
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean() ;
filterRegistrationBean.setFilter(new XssFilter());
filterRegistrationBean.setName("xssFilter");
filterRegistrationBean.setOrder(3); //优先级 第三
//匹配链接
filterRegistrationBean.setUrlPatterns(Arrays.asList(urlPatterns.split(",")));
//自定义参数
Map<String, String> initParameters = new HashMap<String, String>();
initParameters.put("excludes", excludes);
initParameters.put("enabled", enabled);
filterRegistrationBean.setInitParameters(initParameters);
return filterRegistrationBean;
}
}
5.关于配置文件
#DH加解密过滤
dhCheck:
# 过滤开关(false则为关闭过滤器,过滤器将失效)
dhIsOpen: true
# 暴露链接(不需要过滤的链接,多个用逗号分隔)
dhExposeUrls: /PublicAPI/getKeys,/PublicAPI/geta
#匹配链接(需要过滤的链接,一定会过滤)
dhUrlPatterns: /DocAppointSpecial/*,/AppointCRoom/*,/ToolList/getAppointTimes
客户端整改
切记:某些整改在服务端已经处理过了,但是必须两端都有处理,只是服务端或只是客户端做了处理都不行!!!
1.敏感信息脱敏处理
敏感信息需要带星号处理,如: 手机号:13000000000 处理为 130****0000,身份证以及其他敏感数据也需要同样处理;
注:手机号、身份证、各种卡号等为必须脱敏处理;
2.参数验证
需要严格的参数验证,如字符串必须是数字等参数的验证,如手机号必须为手机号格式等;
3.密码规则
如有注册账号功能,且需设定密码规则:不少于12位,且必须包含,符号,数字,大写字母,小写字母其中3种;