跨站脚本攻击XSS(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从而达到恶意攻击用户的目的。XSS攻击针对的是用户层面的攻击!
XSS分为:存储型 、反射型 、DOM型XSS
存储型XSS:存储型XSS,持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,插入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃cookie
反射型XSS:非持久化,需要欺骗用户自己去点击链接才能触发XSS代码(服务器中没有这样的页面和内容),一般容易出现在搜索页面
DOM型XSS:不经过后端,DOM-XSS漏洞是基于文档对象模型(Document Objeet Model,DOM)的一种漏洞,DOM-XSS是通过url传入参数去控制触发的,其实也属于反射型XSS。
<1>限制用户输入,表单数据规定值得类型,例如年龄只能是int,name为字母数字组合。
<2>对数据进行html encode处理。
<3>过滤或移除特殊的html标签。
<4>过滤javascript事件的标签。
SQL注入(SQLi)是一种注入攻击,,可以执行恶意SQL语句。它通过将任意SQL代码插入数据库查询,使攻击者能够完全控制Web应用程序后面的数据库服务器。攻击者可以使用SQL注入漏洞绕过应用程序安全措施;可以绕过网页或Web应用程序的身份验证和授权,并检索整个SQL数据库的内容;还可以使用SQL注入来添加,修改和删除数据库中的记录。
__
SQL注入漏洞可能会影响使用SQL数据库(如MySQL,Oracle,SQL Server或其他)的任何网站或Web应用程序。犯罪分子可能会利用它来未经授权访问用户的敏感数据:客户信息,个人数据,商业机密,知识产权等。SQL注入攻击是最古老,最流行,最危险的Web应用程序漏洞之一。
1.数据库信息泄漏:数据库中存放的用户的隐私信息的泄露。
2.网页篡改:通过操作数据库对特定网页进行篡改。
3.网站被挂马,传播恶意软件:修改数据库一些字段的值,嵌入网马链接,进行挂马攻击。
4.数据库被恶意操作:数据库服务器被攻击,数据库的系统管理员帐户被窜改。
5.服务器被远程控制,被安装后门。经由数据库服务器提供的操作系统支持,让黑客得以修改或 控制操作系统。
6.破坏硬盘数据,瘫痪全系统。
<1>过滤用户输入,对用户输入输出进行校验
<2>不使用动态拼装SQL,使用参数化的SQL或者使用存储过程进行数据查询存取。
<3>封装异常信息
<4>设立数据库连接分级授权
一: #能防止sql注入, $不能
① 使用#: Mybatis的sql是一个具有“输入+输出”功能,类似于函数的结构,如下:
<select id="getBlogById" resultType="Blog" parameterType=”int”>
select id,title,author,content
from blog where id=#{id}
</select>
打印出执行的sql语句,会看到sql是这样的:
select id,title,author,content from blog where id = ?
不管输入什么参数,打印出的sql都是这样的。这是因为mybatis启用了预编译功能,在sql执行前,会先将上面的sql发送给数据库进行编译,执行时,直接使用编译好的sql,替换占位符“?”就可以了。因为sql注入只能对编译过程起作用,所以这样的方式就很好地避免了sql注入的问题。
mybatis是如何做到sql预编译的呢?
其实在框架底层,是jdbc中的PreparedStatement类在起作用,PreparedStatement是我们很熟悉的Statement的子类,它的对象包含了编译好的sql语句。这种“准备好”的方式不仅能提高安全性,而且在多次执行一个sql时,能够提高效率,原因是sql已编译好,再次执行时无需再编译。
② 使用$:
<select id="orderBlog" resultType="Blog" parameterType=”map”>
select id,title,author,content
from blog order by ${orderParam}
</select>
仔细观察,内联参数的格式由“#{xxx}”变为了${xxx}。如果我们给参数“orderParam”赋值为”id”,将sql打印出来,是这样的:
select id,title,author,contet from blog order by id
显然,这样是无法阻止sql注入的。在mybatis中,” x x x ” 这 样 格 式 的 参 数 会 直 接 参 与 s q l 编 译 , 从 而 不 能 避 免 注 入 攻 击 。 但 涉 及 到 动 态 表 名 和 列 名 时 , 只 能 使 用 “ {xxx}”这样格式的参数会直接参与sql编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“ xxx”这样格式的参数会直接参与sql编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“{xxx}”这样的参数格式,所以,这样的参数需要我们在代码中手工进行处理来防止注入。
结论:在编写mybatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
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.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class XssFilter implements Filter {
// 无权限警告页面
// private final String warnPageurl = "null";
Logger log = LoggerFactory.getLogger(this.getClass());
/**
* 排除部分URL不做过滤
*/
// 忽略权限检查的JSP
private final String[] excludeUrls = new String[]{
"null"
};
// 忽略权限检查的URL
/* private final String[] excludePermissionUrls = new String[]{
"null"
};*/
/**
* 公告新增、修改用到富文本,对标签进行转义
*/
// private List noticeUrls = new ArrayList();
public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) arg0;
HttpServletResponse response = (HttpServletResponse) arg1;
String pathInfo = req.getPathInfo() == null ? "" : req.getPathInfo();
//获取请求url的后两层
String url = req.getServletPath() + pathInfo;
//获取请求你ip后的全部路径
String uri = req.getRequestURI();
// if("".length() < 10){
// arg2.doFilter(arg0, arg1);
// return;
// }
// boolean isNoticeUrl = false;
// boolean isExcludePermissionUrl = false;//无需检查权限
// req.getParameterMap();
//注入xss过滤器实例
XssHttpServletRequestWraper reqW = new XssHttpServletRequestWraper(req);
// setHttpOnly(req,response);
// 排除部分URL不做过滤。
for (String str : excludeUrls) {
if (uri.indexOf(str) >= 0) {
arg2.doFilter(reqW, response);
return;
}
}
//忽略权限检查
// for (String st : excludePermissionUrls) {
// if (uri.indexOf(st) >= 0) {
// isExcludePermissionUrl = true;
// break;
// }
// }
//重点检查、包含密码的参数
// for (String st : noticeUrls) {
// if (uri.indexOf(st) >= 0) {
// isNoticeUrl = true;
// break;
// }
// }
// 获取请求所有参数,校验防止SQL注入,防止XSS漏洞
Enumeration<?> params = req.getParameterNames();
String paramN = null;
while (params.hasMoreElements()) {
paramN = (String) params.nextElement();
String paramVale = req.getParameter(paramN);
// if (!paramN.toLowerCase().contains("password")) {
// }
//if (isNoticeUrl) {
paramVale = xssEncode(paramVale);
//}
// 校验是否存在SQL注入信息
if (checkSQLInject(paramVale, url)) {
errorResponse(response, paramN,"输入项中不能包含非法字符。");
log.error("输入项中不能包含非法字符! URI="+uri+";paramName="+ paramN+";paramVale="+ paramVale);
// System.out.println("输入项中不能包含非法字符。param="+ paramVale +";"+ uri);
return;
}
}
arg2.doFilter(reqW, response); //bgh 2018/05/30 修改
}
//用来响应错误信息
private void errorResponse(HttpServletResponse response, String paramNm,String warning) throws IOException {
Map<String,Objet> map = new HashMap<>();
map.put("code",50000);
map.put("message",xssEncode(warning));
map.put("paramName",paramNm);
response.setContentType("text/json;charset=UTF-8");
PrintWriter out = response.getWriter();
out.write(fromObject.toString());
out.plush();
out.close();
}
public void destroy() {
}
public void init(FilterConfig filterconfig1) throws ServletException {
// 读取文件
// String path = XssFilter.class.getResource("/").getFile();
// //excludeUrls = readFile(path + "xssWhite.txt");
// noticeUrls.add("notice!saveNotice");
// noticeUrls.add("notice!updateNoticeById");
}
//是否包含特殊字符,特殊字符替换
private String xssEncode(String s) {
if (s == null || s.isEmpty()) {
return s;
}
//不是线程安全(不能同步访问),效率比StringBuffer高,适合在单线程下使用
//容量大小的基础上+16字节
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;
case '#':
sb.append('#');// 全角井号
break;
case '(':
sb.append('(');//
break;
case ')':
sb.append(')');//
break;
case ';':
sb.append(';');//
break;
default:
sb.append(c);
break;
}
}
return sb.toString();
}
/**
*
* 检查是否存在非法字符,防止SQL注入
*
* @param str
* 被检查的字符串
* @return ture-字符串中存在非法字符,false-不存在非法字符
*/
public static boolean checkSQLInject(String str, String url) {
// 如果传入空串则认为不存在非法字符
if (StringUtils.isEmpty(str)) {
return false;
}
// 判断黑名单
String[] inj_stra = { "script", "mid", "master", "truncate", "insert", "select", "delete", "update", "declare", "drop", "grant",
"iframe", "'", "onreadystatechange", "alert", "atestu", ";", "--", "xss", " and ", " or ", "(", ")", "eval", "from dual",
"\\", "svg111", "confirm", "prompt", "onload", "onmouseover", "onfocus", "onerror", "exec", "sleep" , "count" , "*" , "%" ,
"chr" , "char" , "+" , "roomid" , "org_no" , "cxtj" };
str = str.toLowerCase(); // sql不区分大小写
for (int i = 0; i < inj_stra.length; i++) {
if (str.indexOf(inj_stra[i]) >= 0) {
return true;
}
}
return false;
}
}
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class XssHttpServletRequestWraper extends HttpServletRequestWrapper {
Logger log = LoggerFactory.getLogger(this.getClass());
public XssHttpServletRequestWraper() {
super(null);
}
public XssHttpServletRequestWraper(HttpServletRequest httpservletrequest) {
super(httpservletrequest);
}
public String[] getParameterValues(String s) {
String str[] = super.getParameterValues(s);
if (str == null) {
return null;
}
int i = str.length;
String as1[] = new String[i];
for (int j = 0; j < i; j++) {
//System.out.println("getParameterValues:"+str[j]);
as1[j] = cleanXSS(cleanSQLInject(str[j]));
}
return as1;
}
public String getParameter(String s) {
String s1 = super.getParameter(s);
//System.out.println("getParameter:"+s1);
if (s1 == null) {
return null;
} else {
return cleanXSS(cleanSQLInject(s1));
}
}
//bgh 2018/05/30 新增
/**-----------------------start--------------------------*/
public Map<String,String[]> getParameterMap(){
HashMap<String,String[]> paramMap = (HashMap<String,String[]>)super.getParameterMap();
paramMap = (HashMap<String,String[]>) paramMap.clone();
for(Iterator iterator = (Iterator) paramMap.entrySet().iterator();iterator.hasNext();){
Map.Entry<String, String[]> entry = (Map.Entry<String, String[]>) iterator.next();
String[] values = entry.getValue();
for(int i=0;i<values.length;i++){
if(values[i]!=null){
values[i] = cleanXSS(cleanSQLInject(values[i]));
}
}
entry.setValue(values);
}
return paramMap;
}
/**-----------------------end--------------------------*/
public String getHeader(String s) {
String s1 = super.getHeader(s);
//System.out.println("getHeader:"+s1);
if (s1 == null) {
return null;
} else {
return cleanXSS(cleanSQLInject(s1));
}
}
public String cleanXSS(String src) {
String temp = src;
src = src.replaceAll("<", "<").replaceAll(">", ">");
src = src.replaceAll("\\(", "(").replaceAll("\\)", ")");
src = src.replaceAll("'", "'");
src = src.replaceAll(";", ";");
//bgh 2018/05/30 新增
/**-----------------------start--------------------------*/
src = src.replaceAll("<", "& lt;").replaceAll(">", "& gt;");
src = src.replaceAll("\\(", "& #40;").replaceAll("\\)", "& #41");
src = src.replaceAll("eval\\((.*)\\)", "");
src = src.replaceAll("[\\\"\\\'][\\s]*javascript:(.*)[\\\"\\\']", "\"\"");
src = src.replaceAll("script", "");
src = src.replaceAll("link", "");
src = src.replaceAll("frame", "");
/**-----------------------end--------------------------*/
Pattern pattern = Pattern.compile("(eval\\((.*)\\)|script)",
Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(src);
src = matcher.replaceAll("");
pattern = Pattern.compile("[\\\"\\'][\\s]*javascript:(.*)[\\\"\\']",
Pattern.CASE_INSENSITIVE);
matcher = pattern.matcher(src);
src = matcher.replaceAll("\"\"");
// 增加脚本
src = src.replaceAll("script", "").replaceAll(";", "")
/*.replaceAll("\"", "").replaceAll("@", "")*/
.replaceAll("0x0d", "").replaceAll("0x0a", "");
if (!temp.equals(src)) {
// System.out.println("输入信息存在xss攻击!");
// System.out.println("原始输入信息-->" + temp);
// System.out.println("处理后信息-->" + src);
log.error("输入信息存在xss攻击!");
log.error("原始输入信息-->" + temp);
log.error("处理后信息-->" + src);
//this.getSession().setAttribute("xss", "输入信息存在xss攻击!");
//this.setAttribute("xss", "输入信息存在xss攻击!");
}
return src;
}
// 需要增加通配,过滤大小写组合
public String cleanSQLInject(String src) {
String lowSrc = src.toLowerCase();
String temp = src;
String lowSrcAfter = lowSrc.replaceAll("insert ", "forbidI")
.replaceAll("select ", "forbidS")
.replaceAll("update ", "forbidU")
.replaceAll("delete ", "forbidD").replaceAll("and", "forbidA")
.replaceAll("or ", "forbidO");
if (!temp.equals(src)) {
// System.out.println("输入信息存在SQL攻击!");
// System.out.println("原始输入信息-->" + temp);
// System.out.println("处理后信息-->" + lowSrc);
log.error("输入信息存在SQL攻击!");
log.error("原始输入信息-->" + temp);
log.error("处理后信息-->" + lowSrc);
//this.getSession().setAttribute("xss", "输入信息存在xss攻击!");
}
if(lowSrcAfter.equals(lowSrc)){
return src;
}else{
return lowSrcAfter;
}
}
}