1.配置web.xml的filter
<filter>
<filter-name>xssFilter</filter-name>
<display-name>xssFilter</display-name>
<filter-class>com.boco.framework.common.web.struts.XSSFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>xssFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter>
<filter-name>antiSqlFilter</filter-name>
<display-name>antiSqlFilter</display-name>
<filter-class>com.boco.framework.security.filter.AntiSqlInjectionfilter</filter-class>
</filter>
<filter-mapping>
<filter-name>antiSqlFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
2.新建一个XSSFilter
public class XSSFilter implements Filter {
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
XssHttpServletRequestWrapper xssRequest = new XssHttpServletRequestWrapper(
(HttpServletRequest) request);
chain.doFilter(xssRequest, response);
}
public void destroy() {
}
}
public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper {
public String escape(String 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();
}
/**
* 将容易引起xss漏洞的半角字符直接替换成全角字符
*
* @param s
* @return
*/
public String xssEncode(String s) {
if (s == null || s.isEmpty()) {
return s;
}
String result = stripXSS(s);
//导致委托代理中委托书富文本中'<'被替换为全角'<'不能生产显示,故取消此半角转全角功能
// if (null != result) {
// result = escape(result);
// }
return result;
}
private String stripXSS(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("<script>(.*?)</script>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid anything in a src='...' type of expression
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
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("</script>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Remove any lonesome <script ...> tag
scriptPattern = Pattern.compile("<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 expression(...) expressions
scriptPattern = Pattern.compile("expression\\((.*?)\\)", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid javascript:... expressions
scriptPattern = Pattern.compile("javascript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid vbscript:... expressions
scriptPattern = Pattern.compile("vbscript:", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Avoid οnlοad= expressions
scriptPattern = Pattern.compile("onload(.*?)=", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("<iframe>(.*?)</iframe>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
scriptPattern = Pattern.compile("</iframe>", Pattern.CASE_INSENSITIVE);
value = scriptPattern.matcher(value).replaceAll("");
// Remove any lonesome <script ...> tag
scriptPattern = Pattern.compile("<iframe(.*?)>", Pattern.CASE_INSENSITIVE | Pattern.MULTILINE | Pattern.DOTALL);
value = scriptPattern.matcher(value).replaceAll("");
}
return value;
}
public XssHttpServletRequestWrapper(HttpServletRequest request) {
super(request);
}
@Override
public String getParameter(String name) {
return xssEncode(super.getParameter(name));
}
@Override
public String getHeader(String name) {
return xssEncode(super.getHeader(name));
}
@Override
public String[] getParameterValues(String name) {
String[] values = super.getParameterValues(name);
if (values == null) {
return null;
}
String[] newValues = new String[values.length];
for (int i = 0; i < values.length; i++) {
newValues[i] = xssEncode(values[i]);
}
return newValues;
}
}
3.新建一个filter过滤相关字符
在获取请求参数时POST和GET获取方式存在差异
一 获取URL:
getRequestURL()
二 获取参数列表:
1.getQueryString()
只适用于GET,比如客户端发送http://localhost/testServlet?a=b&c=d&e=f,通过request.getQueryString()得到的是a=b&c=d&e=f.
2.getParameter()
GET和POST都可以使用
但如果是POST请求要根据<form>表单提交数据的编码方式来确定能否使用.
当编码方式是(application/x- www-form-urlencoded)时才能使用.
这种编码方式(application/x-www-form-urlencoded)虽然简单,但对于传输大块的二进制数据显得力不从心.
对于传输大块的二进制数这类数据,浏览器采用了另一种编码方式("multipart/form-data"),这时就需要使用下面的两种方法.
3.getInputStream()
4.getReader()
上面两种方法获取的是Http请求包的包体,因为GET方式请求一般不包含包体.所以上面两种方法一般用于POST请求获取参数.
需要注意的是:
request.getParameter()、 request.getInputStream()、request.getReader()这三种方法是有冲突的,因为流只能被读一次。
比如:
当form表单内容采用 enctype=application/x-www-form-urlencoded编码时,先通过调用request.getParameter()方法得到参数后,
再调用request.getInputStream()或request.getReader()已经得不到流中的内容,
因为在调用 request.getParameter()时系统可能对表单中提交的数据以流的形式读了一次,反之亦然。
当form表单内容采用 enctype=multipart/form-data编码时,即使先调用request.getParameter()也得不到数据,
所以这时调用request.getParameter()方法对 request.getInputStream()或request.getReader()没有冲突,
即使已经调用了 request.getParameter()方法也可以通过调用request.getInputStream()或request.getReader()得到表单中的数据,
而request.getInputStream()和request.getReader()在同一个响应中是不能混合使用的,如果混合使用就会抛异常。
ServletInputStream ris = req.getInputStream();
StringBuilder content = new StringBuilder();
byte[] b = new byte[1024];
int lens = -1;
while ((lens = ris.read(b)) > 0) {
content.append(new String(b, 0, lens));
}
String strcont = content.toString();// 内容
/**
* 过滤器
* 功能:<br>
* 1-防SQL注入<br>
* 2-防跨域(XSS)<br>
* 3-request参数值去字符两边空白字符(暂时只针对查询参数)<br>
* 4-针对数量和单价和金额最大值进行过滤<br>
* @author szj
* @update 2016-07-14
*/
public class AntiSqlInjectionfilter implements Filter {
private static Pattern SCRIPT_PATTERN = Pattern.compile("<script.*>.*<\\/script\\s*>");
private CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
// private static Pattern HTML_PATTERN = Pattern.compile("<[^>]+>");
public AntiSqlInjectionfilter() {
}
public void destroy() {
}
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain) throws IOException,
ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// HttpServletResponse resp = (HttpServletResponse) response;
// response.setCharacterEncoding("GBK");
// resp.setHeader("Pragma", "no-cache");
// resp.setHeader("Cache-Control", "no-cache");
// resp.setHeader("Cache-Control", "no-store");
// resp.setDateHeader("Expires", 0);
//判断enctype属性是否为multipart/form-data
String contentType = req.getContentType();//获取请求的content-type
String requestURI = req.getRequestURI();
if(StringUtils.isNotBlank(contentType) && contentType.contains("multipart/form-data") &&
StringUtils.isNotBlank(requestURI) && !requestURI.contains("progressBarResponse.do")){//文件上传请求 *特殊请求(除去Excel导入的提交方式)
MultipartHttpServletRequest multiReq = multipartResolver.resolveMultipart(req);
request = multiReq;//将转化后的reuqest赋值到过滤链中的参数 *重要
}
//是否需要对参数进行去空格处理
boolean isTrimFalg = false;
try {
String qs = req.getQueryString();
try {//URL反编码,防止req.getParameterMap()失效
if(!StringUtils.isNotEmpty(qs)){
qs = "";
}
//获取POST 请求当中的参数
ServletInputStream ris = req.getInputStream();
if(ris!=null){
StringBuilder content = new StringBuilder();
byte[] b = new byte[1024];
int lens = -1;
while ((lens = ris.read(b)) > 0) {
content.append(new String(b, 0, lens));
}
qs += content.toString();// 内容
}
qs = URLDecoder.decode(qs,"UTF-8");
}
catch (Exception e) {
throw new Exception("请求参数中包含了非法字符。");
}
if(qs.contains("<") || qs.contains(">")
|| qs.contains("%")
|| qs.contains("'")
|| qs.contains(";")
|| qs.contains("$")
|| qs.contains("\"")
|| qs.contains("+")
|| qs.contains("(")
|| qs.contains(")")
|| qs.contains("cr")){
throw new Exception("请求参数中包含了非法字符。");
}
Map paramMap = req.getParameterMap();
int flag = 0;
String lowStr = null;
Set keSet = paramMap.entrySet();
for (Iterator itr = keSet.iterator(); itr.hasNext();) {
Map.Entry me = (Map.Entry) itr.next();
Object ok = me.getKey();
if(ok instanceof String) {
String key = (String)ok;
if(key.toUpperCase().contains("MAIL")){
continue;
}
if(key.contains("Parameter")){//暂时只针对查询参数去空格处理
isTrimFalg=true;
}
}
Object ov = me.getValue();
String[] value = new String[1];
if (ov instanceof String[]) {
value = (String[]) ov;
}
else {
value[0] = ov.toString();
}
for (int k = 0; k < value.length; k++) {
//System.out.println("key:"+ok+" "+"value:"+value[k]);
lowStr = URLDecoder.decode(value[k].toLowerCase(),"UTF-8");
// 过滤html标签 交办中使用了html脚本,不能屏蔽。否则无法交办
// Matcher mHtml = HTML_PATTERN.matcher(lowStr);
// if (mHtml.find()) {
// throw new Exception("请求参数中包含了非法脚本。");
// }
//过滤html相关标签
if(lowStr.contains("confirm") || lowStr.contains("src=")){
throw new Exception("请求参数中包含了非法html脚本。");
}
// 过滤script脚本
Matcher m = SCRIPT_PATTERN.matcher(lowStr);
if (m.find()) {
throw new Exception("请求参数中包含了非法的script脚本。");
}
// 过滤XSS跨域脚本
if(stripXSS(lowStr)){
throw new Exception("请求参数中包含了非法的XSS跨站点脚本。");
}
// 过滤特殊字符
if (lowStr.contains("|")
|| lowStr.contains("&")
|| lowStr.contains(";")
|| lowStr.contains("$")
|| lowStr.contains("%")
|| lowStr.contains("@")
|| lowStr.contains("\"")
// || lowStr.contains("+")
|| lowStr.contains("\'")
// || lowStr.contains("\\")
|| lowStr.contains("'")
// || lowStr.contains("<")
// || lowStr.contains(">")
// || lowStr.contains("(")
// || lowStr.contains(")")
|| lowStr.contains("cr")
// || lowStr.contains(",")
// || lowStr.contains(".")
|| isContains4Blank(lowStr,"lf",1)
|| lowStr.contains("document")
|| lowStr.contains("eval")
) {
throw new Exception("拒绝访问:参数【"+lowStr+"】含特殊字符。");
}
// 过滤sql转换函数
if (lowStr.contains("ascii(") || lowStr.contains("ascii (")
|| lowStr.contains("chr(")
|| lowStr.contains("chr (")) {
throw new Exception("请求参数中包含了非法的sql脚本转换函数关键字。");
}
// 过滤sql关键字
// if (lowStr.contains("alter ")
// || lowStr.contains("create ")
// || lowStr.contains("truncate ")
// || lowStr.contains("drop ")
// || lowStr.contains("lock table")
// || lowStr.contains("insert ")
// || lowStr.contains("update ")
// || lowStr.contains("delete ")
// || lowStr.contains("select ")
// || lowStr.contains("grant ")) {
// throw new Exception("请求参数中包含了非法的sql脚本关键字。");
// }
if (isContains4Blank(lowStr,"alter",1)
|| isContains4Blank(lowStr,"create",1)
|| isContains4Blank(lowStr,"truncate",1)
|| isContains4Blank(lowStr,"drop",1)
|| isContains4Blank(lowStr,"lock",1)
|| isContains4Blank(lowStr,"table", 2)
|| isContains4Blank(lowStr,"insert",1)
|| isContains4Blank(lowStr,"update",1)
|| isContains4Blank(lowStr,"delete",1)
|| isContains4Blank(lowStr,"select",1)
|| isContains4Blank(lowStr,"grant",1)
|| isContains4Blank(lowStr,"like",1)
|| isContains4Blank(lowStr,"or", 3)
|| isContains4Blank(lowStr,"and", 3)) {
throw new Exception("请求参数中包含了非法的sql脚本关键字。");
}
//针对数量和单价和金额最大值进行过滤
String key = (String) ok;
if (key.toLowerCase().contains("amount")
|| key.toLowerCase().contains("price")
|| key.toLowerCase().contains("money")
|| key.toLowerCase().contains("presumw")) {
Double db = null;
try {
db = Double.parseDouble(lowStr);
}
catch (Exception e) {
}
if (db != null && db.longValue() > 99999999999999l) {
throw new Exception("您录入的数字【" + lowStr + "】过大。请修改!");
}
}
}
}
// 暂时只针对查询参数去空格处理
if (isTrimFalg) {
ParameterRequestWrapper requestWrapper = new ParameterRequestWrapper((HttpServletRequest) request);
chain.doFilter(requestWrapper, response);
}else{
chain.doFilter(request, response);
}
}
catch (Exception e) {
e.printStackTrace();
String msg = e.getMessage();
//2015.12.28 错误提示优化
if(msg.contains("Exception:")){
msg = msg.split("Exception:")[1].toString();
}
request.setAttribute("message", msg);
request.getRequestDispatcher("/error.jsp")
.forward(request, response);
}
}
/**
* XSS跨域校验
* @param value 校验值
* @return 是否存在跨域脚本:true是,false否
*/
private boolean stripXSS(String value) {
String cleanValue = null;
int rsFlag = 0;
if (value != null) {
cleanValue = Normalizer.normalize(value, Normalizer.Form.NFD);
// Avoid null characters
cleanValue = cleanValue.replaceAll("\0", "");
// Avoid anything between script tags
Pattern scriptPattern = Pattern.compile("<script>(.*?)</script>",
Pattern.CASE_INSENSITIVE);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
// Avoid anything in a src='...' type of expression
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\'(.*?)\\\'",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
| Pattern.DOTALL);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
scriptPattern = Pattern.compile("src[\r\n]*=[\r\n]*\\\"(.*?)\\\"",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
| Pattern.DOTALL);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
// Remove any lonesome </script> tag
scriptPattern = Pattern.compile("</script>",
Pattern.CASE_INSENSITIVE);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
// Remove any lonesome <script ...> tag
scriptPattern = Pattern.compile("<script(.*?)>",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
| Pattern.DOTALL);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
// Avoid eval(...) expressions
scriptPattern = Pattern.compile("eval\\((.*?)\\)",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
| Pattern.DOTALL);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
// Avoid expression(...) expressions
scriptPattern = Pattern.compile("expression\\((.*?)\\)",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
| Pattern.DOTALL);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
// Avoid javascript:... expressions
scriptPattern = Pattern.compile("javascript:",
Pattern.CASE_INSENSITIVE);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
// Avoid vbscript:... expressions
scriptPattern = Pattern.compile("vbscript:",
Pattern.CASE_INSENSITIVE);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
scriptPattern = Pattern.compile("jscript:",
Pattern.CASE_INSENSITIVE);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
// Avoid οnlοad= expressions
scriptPattern = Pattern.compile("onload(.*?)=",
Pattern.CASE_INSENSITIVE | Pattern.MULTILINE
| Pattern.DOTALL);
if (scriptPattern.matcher(cleanValue).find()) {
rsFlag++;
}
}
return rsFlag > 0;
}
/**
* 判断是否包含测试字符+空白的情况
* @param str url参数
* @param testString 测试是否包含的字符串
* @param 1 检测后一位是否是空白;2 检测前一位是否是空白;3 检测前、后一位是否是空白
* @return
*/
private boolean isContains4Blank(String str,String testString,int type){
if(str.contains(testString)){
String[] ss = str.split(testString);
if(type == 1){
for(int i=1;i<ss.length;i++){
char c = ss[i].charAt(0);
if(c <= ' ' || c == '\''){
return true;
}
}
}
if(type == 2){
for(int i=0;i<ss.length-1;i++){
if(ss[i] != null && ss[i].length()>0){
char c = ss[i].charAt(ss[i].length()-1);
if(c <= ' ' || c == '\''){
return true;
}
}
}
}
if(type == 3){
for(int i=0;i<ss.length-1;i++){
if(ss[i] != null && ss[i].length()>0){
char c = ss[i].charAt(ss[i].length()-1);
char c2 = ss[i+1].charAt(0);
if((c <= ' ' || c == '\'')
&& (c2 <= ' ' || c2 == '\'')){
return true;
}
}
}
}
}
return false;
}
public void init(FilterConfig fConfig) throws ServletException {
}
}