浏览代码

HttpResponse 响应头

Xss 过滤器
405 错误码
master
yaoq 2 年前
父节点
当前提交
7444734590
共有 6 个文件被更改,包括 426 次插入0 次删除
  1. +51
    -0
      smtweb-framework/bpm/src/main/resources/config/application.yaml
  2. +3
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/exception/ExceptionMessage.java
  3. +32
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/filter/HttpSecurityConfig.java
  4. +113
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/filter/HttpSecurityFilter.java
  5. +115
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/filter/XssHttpRequestWrapper.java
  6. +112
    -0
      smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/filter/XssSecurityConfig.java

+ 51
- 0
smtweb-framework/bpm/src/main/resources/config/application.yaml 查看文件

@@ -41,3 +41,54 @@ spring:
- bpm
caffeine:
spec: maximumSize=1024,expireAfterWrite=2h


# http 规则配置
http-config:
xss: #xss 规则
checkHeader: false #是否进行header校验
checkParameter: true #是否进行parameter校验
logIs: true #是否记录日志
chain: true #是否中断请求
replace: true #是否开启特殊字符替换
checkUrl: true #是否开启特殊url校验
regex:
# 匹配含有字符: alert( )
- .*[A|a][L|l][E|e][R|r][T|t](.*).*
# 匹配含有字符: window.location
- .*[W|w][I|i][N|n][D|d][O|o][W|w].[L|l][O|o][C|c][A|a][T|t][I|i][O|o][N|n].*
# 匹配含有字符:style = x:ex pression ( )
- .*[S|s][T|t][Y|y][L|l][E|e]\\s*=.*[X|x]:[E|e][X|x].*[P|p][R|r][E|e][S|s]{1,2}[I|i][O|o][N|n]\\s*\\(.*\\).*
# 匹配含有字符: document.cookie
- .*[D|d][O|o][C|c][U|u][M|m][E|e][N|n][T|t].[C|c][O|o]{2}[K|k][I|i][E|e].*
# 匹配含有字符: eval( )
- .*[E|e][V|v][A|a][L|l](.*).*
# 匹配含有字符: unescape()
- .*[U|u][N|n][E|e][S|s][C|c][A|a][P|p][E|e](.*).*
# 匹配含有字符: execscript( )
- .*[E|e][X|x][E|e][C|c][S|s][C|c][R|r][I|i][P|p][T|t](.*).*
# 匹配含有字符: msgbox( )
- .*[M|m][S|s][G|g][B|b][O|o][X|x](.*).*
# 匹配含有字符: confirm( )
- .*[C|c][O|o][N|n][F|f][I|i][R|r][M|m](.*).*
# 匹配含有字符: prompt( )
- .*[P|p][R|r][O|o][M|m][P|p][T|t](.*).*
# 匹配含有字符: <script> </script>
- .*<[S|s][C|c][R|r][I|i][P|p][T|t]>.*.*</[S|s][C|c][R|r][I|i][P|p][T|t]>.*
# 匹配含有字符: </script>
- .*</[S|s][C|c][R|r][I|i][P|p][T|t]>.*
# 匹配含有字符: <script>
- .*<[S|s][C|c][R|r][I|i][P|p][T|t]>.*
res: # 响应头规则
configs:
{
Set-Cookie: Secure; HttpOnly,
Referrer-Policy: origin-when-cross-origin,
X-Frame-Options: SAMEORIGIN,
X-XSS-Protection: 1;mode=block ,
X-Download-Options: SAMEORIGIN ,
X-Content-Type-Options: nosniff ,
Content-Security-Policy: default-src 'self'; img-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' http://localhost:7102 https://localhost:7102; style-src 'self' 'unsafe-inline'; connect-src 'self';font-src 'self' data:; ,
Strict-Transport-Security: max-age=31536000;includeSubDomains ,
X-Permitted-Cross-Domain-Policies: master-only
}

+ 3
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/exception/ExceptionMessage.java 查看文件

@@ -32,6 +32,9 @@ public class ExceptionMessage {
// 404
public static ExceptionMessage NOT_FOUND_ERROR = new ExceptionMessage(404, "没有找到需要资源!");

// 405
public static ExceptionMessage NOT_ALLOWED_ERROR = new ExceptionMessage(405, "访问被阻断!");

// 通用异常
public static ExceptionMessage INNER_ERROR = new ExceptionMessage(100100, "内部服务错误,请稍后再试!");



+ 32
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/filter/HttpSecurityConfig.java 查看文件

@@ -0,0 +1,32 @@
package cc.smtweb.framework.core.mvc.filter;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
* @Author yaoq
* @Date 2022年07月20日 10:38
* @Description
*/
@Data
@Component
@Configuration
@ConfigurationProperties(prefix = "http-config.res")
public class HttpSecurityConfig {

private Map<String, String> configs;

@Override
public String toString() {
if (configs == null) {
return "";
}
return configs.toString();
}
}



+ 113
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/filter/HttpSecurityFilter.java 查看文件

@@ -0,0 +1,113 @@
package cc.smtweb.framework.core.mvc.filter;

import cc.smtweb.framework.core.common.R;
import cc.smtweb.framework.core.exception.ExceptionMessage;
import cc.smtweb.framework.core.util.JsonUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
* @Author yaoq
* @Date 2022年07月20日 9:28
* @Description
*/
@Slf4j
@Component
public class HttpSecurityFilter extends OncePerRequestFilter {

@Autowired
private HttpSecurityConfig httpConfig;

@Autowired
private XssSecurityConfig xssConfig;

@Override
public void afterPropertiesSet() throws ServletException {

}

@Override
public void initFilterBean() throws ServletException {
log.info("HttpSecurityFilter init begin ");
log.info("XssSecurityRegex:" + xssConfig.getRegexStr());
log.info("HttpSecurityRegex:" + httpConfig.toString());
log.info("HttpSecurityFilter init end ");
}

/**
* 销毁操作
*/
public void destroy() {
System.out.println("HttpSecurityFilter destroy");
}


@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
//httpResponse 响应头
if (httpConfig.getConfigs() != null && httpConfig.getConfigs().size() > 0) {
httpConfig.getConfigs().forEach(response::setHeader);
}
//Xss 校验
XssHttpRequestWrapper xssRequest = new XssHttpRequestWrapper(request, xssConfig);
// 对request信息进行封装并进行校验工作,若校验失败(含非法字符),根据配置信息进行日志记录和请求中断处理
if (xssRequest.validateParameter(response)) {
log.error("XSS校验:请求参数存在XSS攻击行为!");
if (xssConfig.isLogIs()) {
// 记录攻击访问日志
// 可使用数据库、日志、文件等方式
}
if (xssConfig.isChain()) {
R r = R.error("很抱歉,您提交的信息中含有非法信息,可能会对系统安全带来影响,您的请求被阻断!");
if (request.getMethod().equals("GET")) {
r = R.error(ExceptionMessage.NOT_ALLOWED_ERROR.getCode(), ExceptionMessage.NOT_ALLOWED_ERROR.getMsg());
}
writeResponseJson(response, JsonUtil.encodeString(r));
return;
}
}
filterChain.doFilter(xssRequest, response);
}

public void writeResponseJson(HttpServletResponse response, String ret) {
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0L);
response.setContentType("application/json;charset=UTF-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
writer.write(ret);
writer.flush();
} catch (Exception e) {
log.error("response error", e);
} finally {
if (writer != null) {
writer.close();
}
}
}


class GetRequest extends HttpServletRequestWrapper {

public GetRequest(HttpServletRequest request) {
super(request);
}

@Override
public String getMethod() {
return "GET";
}
}
}

+ 115
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/filter/XssHttpRequestWrapper.java 查看文件

@@ -0,0 +1,115 @@
package cc.smtweb.framework.core.mvc.filter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Map;
import java.util.Set;


public class XssHttpRequestWrapper extends HttpServletRequestWrapper {

private XssSecurityConfig config;

/**
* 封装http请求
*
* @param request
*/
public XssHttpRequestWrapper(HttpServletRequest request, XssSecurityConfig config) {
super(request);
this.config = config;
}

@Override
public String getHeader(String name) {
String value = super.getHeader(name);
// 若开启特殊字符替换,对特殊字符进行替换
if (config.isReplace()) {
config.securityReplace(name);
}
return value;
}

@Override
public String getParameter(String name) {
String value = super.getParameter(name);
// 若开启特殊字符替换,对特殊字符进行替换
if (config.isReplace()) {
config.securityReplace(name);
}
return value;
}

/**
* 没有违规的数据,就返回false;
*
* @return
*/
@SuppressWarnings("unchecked")
private boolean checkHeader() {
Enumeration<String> headerParams = this.getHeaderNames();
while (headerParams.hasMoreElements()) {
String headerName = headerParams.nextElement();
String headerValue = this.getHeader(headerName);
if (config.matches(headerValue)) {
//UtilLogger.debug("XSS校验:检测到Header攻击:[" + headerValue + "]");
return true;
}
}
return false;
}

/**
* 没有违规的数据,就返回false;
*
* @return
*/
@SuppressWarnings("unchecked")
private boolean checkParameter() {
Map<String, String[]> submitParams = this.getParameterMap();
Set<String> submitNames = submitParams.keySet();
for (String submitName : submitNames) {
Object submitValues = submitParams.get(submitName);
if (submitValues instanceof String) {
if (config.matches((String) submitValues)) {
//UtilLogger.debug("XSS校验:检测到Parameter攻击:[" + submitValues + "]");
return true;
}
} else if (submitValues instanceof String[]) {
for (String submitValue : (String[]) submitValues) {
if (config.matches((String) submitValue)) {
//UtilLogger.debug("XSS校验:检测到Parameter攻击:[" + submitValue + "]");
return true;
}
}
}
}
return false;
}


/**
* 没有违规的数据,就返回false;
* 若存在违规数据,根据配置信息判断是否跳转到错误页面
*
* @param response
* @return
* @throws IOException
* @throws ServletException
*/
public boolean validateParameter(HttpServletResponse response) throws ServletException, IOException {
// 开始header校验,对header信息进行校验
if (config.isCheckHeader()) {
return this.checkHeader();
}
// 开始parameter校验,对parameter信息进行校验
if (config.isCheckParameter()) {
return this.checkParameter();
}
return false;
}
}

+ 112
- 0
smtweb-framework/core/src/main/java/cc/smtweb/framework/core/mvc/filter/XssSecurityConfig.java 查看文件

@@ -0,0 +1,112 @@
package cc.smtweb.framework.core.mvc.filter;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.regex.Pattern;

/**
* @Author yaoq
* @Date 2022年07月20日 10:38
* @Description
*/
@Slf4j
@Component
@Data
@ConfigurationProperties(prefix = "http-config.xss")
public class XssSecurityConfig implements InitializingBean {

private boolean checkHeader;

private boolean checkParameter;

private boolean logIs;

private boolean chain;

private boolean replace;

private boolean checkUrl;

private List<String> regex;


/**
* 替换非法字符的字符串
*/
private String REPLACEMENT = "";

/**
* FILTER_ERROR_PAGE:过滤后错误页面
*/
private String FILTER_ERROR_PAGE = "/jsp/framework/error/405.jsp";

/**
* REGEX:校验正则表达式
*/
private String regexStr;

/**
* 特殊字符匹配
*/
private Pattern XSS_PATTERN;


@Override
public void afterPropertiesSet() throws Exception {
StringBuffer tempStr = new StringBuffer("^");
regex.forEach(k -> {
tempStr.append(k);
tempStr.append("|");
});
regexStr = tempStr.substring(0, tempStr.length() - 1) + "$";
XSS_PATTERN = Pattern.compile(regexStr);
}

@Override
public String toString() {
return "XssSecurityConfig{" +
"checkHeader=" + checkHeader +
", checkParameter=" + checkParameter +
", logIs=" + logIs +
", chain=" + chain +
", replace=" + replace +
", checkUrl=" + checkUrl +
", regex=" + regex +
'}';
}

/**
* 对非法字符进行替换
*
* @param text
* @return
*/
public String securityReplace(String text) {
if (StringUtils.isEmpty(text)) {
return text;
} else {
return text.replaceAll(regexStr, REPLACEMENT);
}
}

/**
* 匹配字符是否含特殊字符
*
* @param text
* @return
*/
public boolean matches(String text) {
if (StringUtils.isEmpty(text)) {
return false;
}
return XSS_PATTERN.matcher(text).matches();
}
}



正在加载...
取消
保存