SQL注入
SQL注入式攻击是未将代码与数据进行严格的隔离,导致在读取用户数据的时候,错误地把数据作为代码的一部分执行,从而导致一些安全问题。例如:传入"–!#@这些内容到SQL上导致语句错误被执行。
如何预防?
- 过滤用户输入的参数中的特殊字符,从而降低被SQL注入的风险。
- 禁止通过字符串拼接的SQL语句,严格使用参数绑定传入的SQL参数。
- 合理使用数据库访问框架提供的防注入机制。比如MyBatis提供的#{}绑定参数,从而防止SQL注入。同时谨慎使用 , {}, ,{}相当于使用字符串拼接SQL。
XSS过滤
XSS攻击全称跨站脚本攻击,是一种在web应用中的计算机安全漏洞,它允许恶意web用户将代码植入到提供给其它用户使用的页面中。XSS主要用于信息窃取、破坏等目的。
示例
<div>
<h3>xss攻击示例</h3>
<br>测试信息:<%= request.getParameter("message")>
</div>
上面代码从请求中获取message参数输出到页面展示。如果参数内容为
测试信息<script src=http://demo/test-script.js/>
就会执行自己写的test-script.js脚本。
如何预防?
- 通过对用户输入数据做过滤或者转义
- 后端可使用Jsoup框架对用户输入字符串做XSS过滤
- 使用Spring框架提供的HtmlUtils做HTML转义
使用Jsoup实现sql注入和XSS过滤
添加maven依赖
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.9.2</version>
</dependency>
自定义JsoupUtil工具类
public class JsoupUtil {
/**
* 使用自带的basicWithImages 白名单
* 允许的便签有a,b,blockquote,br,cite,code,dd,dl,dt,em,i,li,ol,p,pre,q,small,span,
* strike,strong,sub,sup,u,ul,img
* 以及a标签的href,img标签的src,align,alt,height,width,title属性
*/
private static final Whitelist whitelist = Whitelist.basicWithImages();
/**
* 配置过滤化参数,不对代码进行格式化
*/
private static final Document.OutputSettings outputSettings = new Document.OutputSettings().prettyPrint(false);
private static Logger log = LogManager.getLogger(JsoupUtil.class);
static {
// 富文本编辑时一些样式是使用style来进行实现的,所以需要给所有标签添加style属性
whitelist.addAttributes(":all", "style");
}
public static String clean(String content, boolean method) {
if (method) {
if (StringUtils.isNotBlank(content)) content = content.trim();
content = Jsoup.clean(content, "", whitelist, outputSettings);
} else {
content = content.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
content = content.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41;");
content = content.replaceAll("'", "& #39;");
content = content.replaceAll("eval\\((.*)\\)", "");
content = content.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
content = content.replaceAll("script", "");
content = content.replaceAll("[*]", "[" + "*]");
content = content.replaceAll("[+]", "[" + "+]");
content = content.replaceAll("[?]", "[" + "?]");
}
String[] values = content.split(" ");
String badStr = "'|and|exec|execute|insert|select|delete|update|count|drop|%|chr|mid|master|truncate|" +
"char|declare|sitename|net user|xp_cmdshell|;|or|-|+|like'|and|exec|execute|insert|create|drop|" +
"table|from|grant|use|group_concat|column_name|" +
"information_schema.columns|table_schema|union|where|select|delete|update|order|by|count|" +
"chr|mid|master|truncate|char|declare|or|;|-|--|,|like|//|/|%|#";
String[] badStrs = badStr.split("\\|");
for (int i = 0; i < badStrs.length; i++) {
for (int j = 0; j < values.length; j++) {
if (values[j].equalsIgnoreCase(badStrs[i])) {
values[j] = "forbid";
log.info("防sql注入;参数中包含的字段:{" + badStrs[i] + "}" + "替换后的字段:forbid;");
log.info("参数内容:"+content);
}
}
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < values.length; i++) {
if (i == values.length - 1) sb.append(values[i]);
else sb.append(values[i] + " ");
}
content = sb.toString();
return content;
}
}
创建XssHttpServletRequestWrapper
实现XSS过滤的关键:继承HttpServletRequestWrapper,重写从request内获取参数的方法,在其内调用JsoupUtil的方法,进行参数脱敏处理。
import org.apache.commons.lang3.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
HttpServletRequest orgRequest;
private boolean isIncludeRichText;
private boolean xssMethod;
public XssHttpServletRequestWrapper(HttpServletRequest request, boolean isIncludeRichText,boolean xssMethod) {
super(request);
orgRequest = request;
this.isIncludeRichText = isIncludeRichText;
this.xssMethod=xssMethod;
}
/**
* 覆盖getParameter方法,将参数名和参数值都做xss过滤。
* 如果需要获得原始的值,则通过super.getParameterValues(name)来获取
* getParameterNames,getParameterValues和getParameterMap也可能需要覆盖
*/
@Override
public String getParameter(String name) {
Boolean flag = ("content".equals(name) || name.endsWith("WithHtml"));
if (flag && !isIncludeRichText) {
return super.getParameter(name);
}
name = JsoupUtil.clean(name,xssMethod);
String value = super.getParameter(name);
if (StringUtils.isNotBlank(value)) {
value = JsoupUtil.clean(value,xssMethod);
}
return value;
}
@Override
public String[] getParameterValues(String name) {
String[] arr = super.getParameterValues(name);
if (arr != null) {
for (int i = 0; i < arr.length; i++) {
arr[i] = JsoupUtil.clean(arr[i],xssMethod);
}
}
return arr;
}
/**
* 覆盖getHeader方法,将参数名和参数值都做xss过滤。
* 如果需要获得原始的值,则通过super.getHeaders(name)来获取
* getHeaderNames 也可能需要覆盖
*/
@Override
public String getHeader(String name) {
name = JsoupUtil.clean(name,xssMethod);
String value = super.getHeader(name);
if (StringUtils.isNotBlank(value)) {
value = JsoupUtil.clean(value,xssMethod);
}
return value;
}
//获取最原始的request
public HttpServletRequest getOrgRequest() {
return orgRequest;
}
// 获取最原始的request的静态方法
public static HttpServletRequest getOrgRequest(HttpServletRequest req) {
if (req instanceof XssHttpServletRequestWrapper) {
return ((XssHttpServletRequestWrapper) req).getOrgRequest();
}
return req;
}
}
注册XssFilter
通过注解@Configuration的方式注册XSSFilter,使其生效。
@Configuration
public class XssConfig {
@Value("${xss.method:true}")
private String xssMethod;
/**
* xss过滤拦截器
*/
@Bean
public FilterRegistrationBean xssFilterRegistrationBean() {
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
filterRegistrationBean.setFilter(new XssFilter());
filterRegistrationBean.setOrder(1);
filterRegistrationBean.setEnabled(true);
filterRegistrationBean.addUrlPatterns("/*");
Map<String, String> initParameters = Maps.newHashMap();
//配置不需要参数过滤的请求url
initParameters.put("excludes", "/favicon.ico,/img/*,/js/*,/css/*");
/*isIncludeRichText默认为true,主要用于设置富文本(项目内约束以content为名或以WithHtml结尾)内容是否需要过滤*/
initParameters.put("isIncludeRichText", "true");
// 将值设置到配置文件中:true:过滤js内容 false:替换js内容
initParameters.put("xssMethod", xssMethod);
filterRegistrationBean.setInitParameters(initParameters);
return filterRegistrationBean;
}
}
更多文章
Jenkins安装部署项目全过程
报表功能(三)一行核心代码搞定数据合并
重复请求的幂等接口设计的思考(一)
log4j2自定义动态配置日志
开关配置springboot定时任务
长按二维码关注,阅读我的程序员故事