Commit 0fd1e1c9 by wuwenlong

防XSS注入

parent 0f07eb0f
package com.baosight.hpjx.config;
import com.baosight.hpjx.xss.XssFilter;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author wwl
* @Date 2024/3/11 14:20
*/
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean<XssFilter> xssFilterRegistration() {
FilterRegistrationBean<XssFilter> registration = new FilterRegistrationBean<>();
registration.setFilter(new XssFilter());
registration.addUrlPatterns("/*");
registration.setOrder(1);
return registration;
}
}
\ No newline at end of file
......@@ -2,9 +2,12 @@ package com.baosight.hpjx.util;
import com.alibaba.fastjson.JSONObject;
import com.baosight.iplat4j.core.exception.PlatException;
import org.springframework.util.AntPathMatcher;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.UUID;
......@@ -127,5 +130,65 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
paramsText = URLDecoder.decode(paramsText, "UTF-8");
return JSONObject.parseObject(paramsText).getInnerMap();
}
/**
* 查找指定字符串是否匹配指定字符串列表中的任意一个字符串
*
* @param str 指定字符串
* @param strs 需要检查的字符串数组
* @return 是否匹配
*/
public static boolean matches(String str, List<String> strs)
{
if (isEmpty(str) || isEmpty(strs))
{
return false;
}
for (String pattern : strs)
{
if (isMatch(pattern, str))
{
return true;
}
}
return false;
}
/**
* 判断url是否与规则配置:
* ? 表示单个字符;
* * 表示一层路径内的任意字符串,不可跨层级;
* ** 表示任意层路径;
*
* @param pattern 匹配规则
* @param url 需要匹配的url
* @return
*/
public static boolean isMatch(String pattern, String url)
{
AntPathMatcher matcher = new AntPathMatcher();
return matcher.match(pattern, url);
}
/**
* * 判断一个Collection是否为空, 包含List,Set,Queue
*
* @param coll 要判断的Collection
* @return true:为空 false:非空
*/
public static boolean isEmpty(Collection<?> coll)
{
return isNull(coll) || coll.isEmpty();
}
/**
* * 判断一个对象是否为空
*
* @param object Object
* @return true:为空 false:非空
*/
public static boolean isNull(Object object)
{
return object == null;
}
}
package com.baosight.hpjx.xss;
import com.baosight.hpjx.util.StringUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
/**
* xss过滤
*/
public class XssFilter implements Filter {
//不拦截的地址
private List<String> excludedList = new ArrayList<String>();
@Override
public void init(FilterConfig config) throws ServletException {
/*
* 这里只处理了需要拦截的url地址,如果想不拦截某个字段,比如富文本字段,
* 需要自己在XssHttpServletRequestWrapper类中去添加逻辑
*/
excludedList.add("/service/HP*/*");
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
(HttpServletRequest) request);
String url = xssRequest.getServletPath();
if(isExcluded(url)){
chain.doFilter(request, response);
}else{
//使用XSS过滤
chain.doFilter(xssRequest, response);
}
}
@Override
public void destroy() {
}
/**
* 是否不拦截
* @param url 请求地址
* @return true不拦截,false拦截
*/
private boolean isExcluded(String url){
// if(StringUtils.isBlank(url)){
// return false;
// }
if(!StringUtils.matches(url, excludedList)){
return true;
}
// for (String excluded : excludedList) {
// if(!url.contains(excluded)){
// return true;
// }
// }
return false;
}
}
package com.baosight.hpjx.xss;
import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.LinkedHashMap;
import java.util.Map;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
// 没被包装过的HttpServletRequest(特殊场景,需求自己过滤)
HttpServletRequest orgRequest;
// html过滤
private final static HTMLFilter htmlFilter = new HTMLFilter();
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
orgRequest = request;
}
/**
* 过滤json参数
*/
@Override
public ServletInputStream getInputStream() throws IOException {
String contentType = super.getHeader(HttpHeaders.CONTENT_TYPE);
//非json类型,直接返回
if(!(MediaType.APPLICATION_JSON_VALUE.
equalsIgnoreCase(contentType) ||
MediaType.APPLICATION_JSON_UTF8_VALUE.
equalsIgnoreCase(contentType))){
return super.getInputStream();
}
//为空,直接返回
String json = IOUtils.toString(super.getInputStream(), "utf-8");
if (StringUtils.isBlank(json)) {
return super.getInputStream();
}
//xss过滤
json =xssEncode(json);
json = StringEscapeUtils.unescapeHtml4(json);
final ByteArrayInputStream bis =
new ByteArrayInputStream(json.getBytes("utf-8"));
return new ServletInputStream() {
@Override
public boolean isFinished() {
return true;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
@Override
public int read() throws IOException {
return bis.read();
}
};
}
@Override
public String getParameter(String name) {
String value = super.getParameter(xssEncode(name));
if (StringUtils.isNotBlank(value)) {
value =xssEncode(value);
}
return StringEscapeUtils.unescapeHtml4(value);
}
@Override
public String[] getParameterValues(String name) {
String[] parameters = super.getParameterValues(name);
if (parameters == null || parameters.length == 0) {
return null;
}
for (int i = 0; i < parameters.length; i++) {
parameters[i] = xssEncode(parameters[i]);
parameters[i] = StringEscapeUtils.unescapeHtml4(parameters[i]);
}
return parameters;
}
@Override
public Map<String, String[]> getParameterMap() {
Map<String, String[]> map = new LinkedHashMap<>();
Map<String, String[]> parameters = super.getParameterMap();
for (String key : parameters.keySet()) {
String[] values = parameters.get(key);
for (int i = 0; i < values.length; i++) {
values[i] = xssEncode(values[i]);
values[i] = StringEscapeUtils.unescapeHtml4(values[i]);
}
map.put(key, values);
}
return map;
}
@Override
public String getHeader(String name) {
String value = super.getHeader(xssEncode(name));
if (StringUtils.isNotBlank(value)) {
value = xssEncode(value);
}
return StringEscapeUtils.unescapeHtml4(value);
}
private String xssEncode(String input) {
return htmlFilter.filter(input);
}
/**
* 获取最原始的request
*/
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
/**
* 获取最原始的request
*/
public static HttpServletRequest getOrgRequest(HttpServletRequest request) {
if (request instanceof XssHttpServletRequestWrapper) {
return ((XssHttpServletRequestWrapper) request).getOrgRequest();
}
return request;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment