forked from github/dataease
fix: sql注入和xss攻击
This commit is contained in:
parent
44845c3a60
commit
2f06f3720e
@ -0,0 +1,78 @@
|
||||
package io.dataease.commons.filter;
|
||||
|
||||
import io.dataease.commons.holder.ThreadLocalContextHolder;
|
||||
import io.dataease.commons.wrapper.XssAndSqlHttpServletRequestWrapper;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import javax.servlet.*;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.*;
|
||||
|
||||
|
||||
public class SqlFilter implements Filter {
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
|
||||
|
||||
String method = "GET";
|
||||
String param = "";
|
||||
XssAndSqlHttpServletRequestWrapper xssRequest = null;
|
||||
if (request instanceof HttpServletRequest) {
|
||||
method = ((HttpServletRequest) request).getMethod();
|
||||
xssRequest = new XssAndSqlHttpServletRequestWrapper((HttpServletRequest) request);
|
||||
}
|
||||
if ("POST".equalsIgnoreCase(method)) {
|
||||
param = this.getBodyString(xssRequest.getReader());
|
||||
if(StringUtils.isNotBlank(param)){
|
||||
if(xssRequest.checkXSSAndSql(param)){
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
PrintWriter out = response.getWriter();
|
||||
String msg = ThreadLocalContextHolder.getData().toString();
|
||||
out.write(msg);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (xssRequest.checkParameter()) {
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
PrintWriter out = response.getWriter();
|
||||
String msg = ThreadLocalContextHolder.getData().toString();
|
||||
out.write(msg);
|
||||
return;
|
||||
}
|
||||
chain.doFilter(xssRequest, response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
|
||||
}
|
||||
|
||||
// 获取request请求body中参数
|
||||
public static String getBodyString(BufferedReader br) {
|
||||
String inputLine;
|
||||
String str = "";
|
||||
try {
|
||||
while ((inputLine = br.readLine()) != null) {
|
||||
str += inputLine;
|
||||
}
|
||||
br.close();
|
||||
} catch (IOException e) {
|
||||
System.out.println("IOException: " + e);
|
||||
}
|
||||
return str;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
package io.dataease.commons.holder;
|
||||
|
||||
public class ThreadLocalContextHolder {
|
||||
|
||||
|
||||
private static ThreadLocal<Object> sceneThreadLocal = new ThreadLocal<>();
|
||||
|
||||
|
||||
public static Object getData() {
|
||||
return sceneThreadLocal.get();
|
||||
}
|
||||
|
||||
public static void setData(Object data) {
|
||||
if (ThreadLocalContextHolder.sceneThreadLocal == null) {
|
||||
ThreadLocalContextHolder.sceneThreadLocal = new ThreadLocal<>();
|
||||
}
|
||||
ThreadLocalContextHolder.sceneThreadLocal.set(data);
|
||||
}
|
||||
|
||||
public static void clearScene() {
|
||||
setData(null);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,370 @@
|
||||
package io.dataease.commons.wrapper;
|
||||
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Vector;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.ReadListener;
|
||||
import javax.servlet.ServletInputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
|
||||
import io.dataease.commons.holder.ThreadLocalContextHolder;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
|
||||
public class XssAndSqlHttpServletRequestWrapper extends HttpServletRequestWrapper {
|
||||
|
||||
|
||||
HttpServletRequest orgRequest = null;
|
||||
private Map<String, String[]> parameterMap;
|
||||
private final byte[] body; //用于保存读取body中数据
|
||||
|
||||
public XssAndSqlHttpServletRequestWrapper(HttpServletRequest request) throws IOException{
|
||||
super(request);
|
||||
orgRequest = request;
|
||||
parameterMap = request.getParameterMap();
|
||||
body = StreamUtils.copyToByteArray(request.getInputStream());
|
||||
}
|
||||
|
||||
// 重写几个HttpServletRequestWrapper中的方法
|
||||
/**
|
||||
* 获取所有参数名
|
||||
*
|
||||
* @return 返回所有参数名
|
||||
*/
|
||||
@Override
|
||||
public Enumeration<String> getParameterNames() {
|
||||
Vector<String> vector = new Vector<String>(parameterMap.keySet());
|
||||
return vector.elements();
|
||||
}
|
||||
|
||||
/**
|
||||
* 覆盖getParameter方法,将参数名和参数值都做xss & sql过滤。<br/>
|
||||
* 如果需要获得原始的值,则通过super.getParameterValues(name)来获取<br/>
|
||||
* getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
|
||||
*/
|
||||
@Override
|
||||
public String getParameter(String name) {
|
||||
String[] results = parameterMap.get(name);
|
||||
if (results == null || results.length <= 0)
|
||||
return null;
|
||||
else {
|
||||
String value = results[0];
|
||||
if (value != null) {
|
||||
value = xssEncode(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取指定参数名的所有值的数组,如:checkbox的所有数据 接收数组变量 ,如checkobx类型
|
||||
*/
|
||||
@Override
|
||||
public String[] getParameterValues(String name) {
|
||||
String[] results = parameterMap.get(name);
|
||||
if (results == null || results.length <= 0)
|
||||
return null;
|
||||
else {
|
||||
int length = results.length;
|
||||
for (int i = 0; i < length; i++) {
|
||||
results[i] = xssEncode(results[i]);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 覆盖getHeader方法,将参数名和参数值都做xss & sql过滤。<br/>
|
||||
* 如果需要获得原始的值,则通过super.getHeaders(name)来获取<br/>
|
||||
* getHeaderNames 也可能需要覆盖
|
||||
*/
|
||||
@Override
|
||||
public String getHeader(String name) {
|
||||
|
||||
String value = super.getHeader(xssEncode(name));
|
||||
if (value != null) {
|
||||
value = xssEncode(value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将容易引起xss & sql漏洞的半角字符直接替换成全角字符
|
||||
*
|
||||
* @param s
|
||||
* @return
|
||||
*/
|
||||
private static String xssEncode(String s) {
|
||||
if (s == null || s.isEmpty()) {
|
||||
return s;
|
||||
} else {
|
||||
s = stripXSSAndSql(s);
|
||||
}
|
||||
StringBuilder sb = new StringBuilder(s.length() + 16);
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
switch (c) {
|
||||
case '>':
|
||||
sb.append(">");// 转义大于号
|
||||
break;
|
||||
case '<':
|
||||
sb.append("<");// 转义小于号
|
||||
break;
|
||||
// case '\'':
|
||||
// sb.append("'");// 转义单引号
|
||||
// break;
|
||||
// case '\"':
|
||||
// sb.append(""");// 转义双引号
|
||||
// break;
|
||||
case '&':
|
||||
sb.append("&");// 转义&
|
||||
break;
|
||||
case '#':
|
||||
sb.append("#");// 转义#
|
||||
break;
|
||||
default:
|
||||
sb.append(c);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最原始的request
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public HttpServletRequest getOrgRequest() {
|
||||
return orgRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最原始的request的静态方法
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
|
||||
if (req instanceof XssAndSqlHttpServletRequestWrapper) {
|
||||
return ((XssAndSqlHttpServletRequestWrapper) req).getOrgRequest();
|
||||
}
|
||||
|
||||
return req;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* 防止xss跨脚本攻击(替换,根据实际情况调整)
|
||||
*/
|
||||
|
||||
public static String stripXSSAndSql(String value) {
|
||||
if (value != null) {
|
||||
// NOTE: It's highly recommended to use the ESAPI library and
|
||||
// uncomment the following line to
|
||||
// avoid encoded attacks.
|
||||
// value = ESAPI.encoder().canonicalize(value);
|
||||
// Avoid null characters
|
||||
/** value = value.replaceAll("", ""); ***/
|
||||
// Avoid anything between script tags
|
||||
Pattern scriptPattern = Pattern.compile(
|
||||
"<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid anything in a
|
||||
// src="http://www.yihaomen.com/article/java/..." type of
|
||||
// e-xpression
|
||||
scriptPattern = Pattern.compile("src[\r\n| | ]*=[\r\n| | ]*[\\\"|\\\'](.*?)[\\\"|\\\']",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Remove any lonesome </script> tag
|
||||
scriptPattern = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Remove any lonesome <script ...> tag
|
||||
scriptPattern = Pattern.compile("<[\r\n| | ]*script(.*?)>",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid eval(...) expressions
|
||||
scriptPattern = Pattern.compile("eval\\((.*?)\\)",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid e-xpression(...) expressions
|
||||
scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid javascript:... expressions
|
||||
scriptPattern = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid vbscript:... expressions
|
||||
scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
// Avoid onload= expressions
|
||||
scriptPattern = Pattern.compile("onload(.*?)=",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
value = scriptPattern.matcher(value).replaceAll("");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static boolean checkSqlInjection(Object obj){
|
||||
Pattern pattern= Pattern.compile("(.*\\=.*\\-\\-.*)|(.*(\\+|\\-).*)|(.*\\w+(%|\\$|#|&)\\w+.*)|(.*\\|\\|.*)|(.*\\s+(and|or)\\s+.*)" +
|
||||
"|(.*\\b(select|update|union|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b.*)");
|
||||
Matcher matcher=pattern.matcher(obj.toString().toLowerCase());
|
||||
return matcher.find();
|
||||
}
|
||||
|
||||
|
||||
public static boolean checkXSSAndSql(String value) {
|
||||
boolean flag = false;
|
||||
|
||||
if (value != null) {
|
||||
boolean b = checkSqlInjection(value);
|
||||
if(b) {
|
||||
ThreadLocalContextHolder.setData("包含SQL注入的参数,请检查参数!");
|
||||
return true;
|
||||
}
|
||||
// NOTE: It's highly recommended to use the ESAPI library and
|
||||
// uncomment the following line to
|
||||
// avoid encoded attacks.
|
||||
// value = ESAPI.encoder().canonicalize(value);
|
||||
// Avoid null characters
|
||||
/** value = value.replaceAll("", ""); ***/
|
||||
// Avoid anything between script tags
|
||||
Pattern scriptPattern = Pattern.compile(
|
||||
"<[\r\n| | ]*script[\r\n| | ]*>(.*?)</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
|
||||
flag = scriptPattern.matcher(value).find();
|
||||
if (flag) {
|
||||
// threadLocal.set("包含XSS攻击脚本,请检查参数!");
|
||||
return flag;
|
||||
}
|
||||
// Avoid anything in a
|
||||
// src="http://www.yihaomen.com/article/java/..." type of
|
||||
// e-xpression
|
||||
scriptPattern = Pattern.compile("src[\r\n| | ]*=[\r\n| | ]*[\\\"|\\\'](.*?)[\\\"|\\\']",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
flag = scriptPattern.matcher(value).find();
|
||||
if (flag) {
|
||||
ThreadLocalContextHolder.setData("包含XSS攻击脚本,请检查参数!");
|
||||
return flag;
|
||||
}
|
||||
// Remove any lonesome </script> tag
|
||||
scriptPattern = Pattern.compile("</[\r\n| | ]*script[\r\n| | ]*>", Pattern.CASE_INSENSITIVE);
|
||||
flag = scriptPattern.matcher(value).find();
|
||||
if (flag) {
|
||||
ThreadLocalContextHolder.setData("包含XSS攻击脚本,请检查参数!");
|
||||
return flag;
|
||||
}
|
||||
// Remove any lonesome <script ...> tag
|
||||
scriptPattern = Pattern.compile("<[\r\n| | ]*script(.*?)>",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
flag = scriptPattern.matcher(value).find();
|
||||
if (flag) {
|
||||
ThreadLocalContextHolder.setData("包含XSS攻击脚本,请检查参数!");
|
||||
return flag;
|
||||
}
|
||||
// Avoid eval(...) expressions
|
||||
scriptPattern = Pattern.compile("eval\\((.*?)\\)",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
flag = scriptPattern.matcher(value).find();
|
||||
if (flag) {
|
||||
ThreadLocalContextHolder.setData("包含XSS攻击脚本,请检查参数!");
|
||||
return flag;
|
||||
}
|
||||
// Avoid e-xpression(...) expressions
|
||||
scriptPattern = Pattern.compile("e-xpression\\((.*?)\\)",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
flag = scriptPattern.matcher(value).find();
|
||||
if (flag) {
|
||||
ThreadLocalContextHolder.setData("包含XSS攻击脚本,请检查参数!");
|
||||
return flag;
|
||||
}
|
||||
// Avoid javascript:... expressions
|
||||
scriptPattern = Pattern.compile("javascript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
|
||||
flag = scriptPattern.matcher(value).find();
|
||||
if (flag) {
|
||||
ThreadLocalContextHolder.setData("包含XSS攻击脚本,请检查参数!");
|
||||
return flag;
|
||||
}
|
||||
// Avoid vbscript:... expressions
|
||||
scriptPattern = Pattern.compile("vbscript[\r\n| | ]*:[\r\n| | ]*", Pattern.CASE_INSENSITIVE);
|
||||
flag = scriptPattern.matcher(value).find();
|
||||
if (flag) {
|
||||
ThreadLocalContextHolder.setData("包含XSS攻击脚本,请检查参数!");
|
||||
return flag;
|
||||
}
|
||||
// Avoid onload= expressions
|
||||
scriptPattern = Pattern.compile("onload(.*?)=",
|
||||
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
|
||||
flag = scriptPattern.matcher(value).find();
|
||||
if (flag) {
|
||||
ThreadLocalContextHolder.setData("包含XSS攻击脚本,请检查参数!");
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
return flag;
|
||||
}
|
||||
|
||||
public final boolean checkParameter() {
|
||||
Map<String, String[]> submitParams = new HashMap(parameterMap);
|
||||
Set<String> submitNames = submitParams.keySet();
|
||||
for (String submitName : submitNames) {
|
||||
Object submitValues = submitParams.get(submitName);
|
||||
if ((submitValues instanceof String)) {
|
||||
if (checkXSSAndSql((String) submitValues)) {
|
||||
return true;
|
||||
}
|
||||
} else if ((submitValues instanceof String[])) {
|
||||
for (String submitValue : (String[])submitValues){
|
||||
if (checkXSSAndSql(submitValue)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BufferedReader getReader() throws IOException {
|
||||
return new BufferedReader(new InputStreamReader(getInputStream()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletInputStream getInputStream() throws IOException {
|
||||
final ByteArrayInputStream bais = new ByteArrayInputStream(body);
|
||||
return new ServletInputStream() {
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return bais.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFinished() {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReady() {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadListener(ReadListener arg0) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
21
backend/src/main/java/io/dataease/config/FilterConfig.java
Normal file
21
backend/src/main/java/io/dataease/config/FilterConfig.java
Normal file
@ -0,0 +1,21 @@
|
||||
package io.dataease.config;
|
||||
|
||||
import io.dataease.commons.filter.SqlFilter;
|
||||
import org.springframework.boot.web.servlet.FilterRegistrationBean;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
@Configuration
|
||||
public class FilterConfig {
|
||||
|
||||
@Bean
|
||||
public FilterRegistrationBean registration(){
|
||||
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
|
||||
filterRegistrationBean.setFilter(new SqlFilter());//实例化Filter类
|
||||
filterRegistrationBean.addUrlPatterns("/*");//设置匹配模式,这里设置为所有,可以按需求设置为"/hello"等等
|
||||
filterRegistrationBean.setName("SqlFilter");//设置过滤器名称
|
||||
filterRegistrationBean.setOrder(1);//设置执行顺序
|
||||
return filterRegistrationBean;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user