場景: WEB程序容易被SQL注入攻擊,攻擊原理是在請求參數中傳入非法字符,造成SQL語句出現出現異常情況,已達到攻擊者想要的結果。
分析: 一般的攻擊者都是在傳入的請求參數上做文章,所以我們重點檢查的是request的參數,判斷request請求參數是否有非法字符,及數據庫關鍵字,如果有即判定爲SQL注入,否則通過。一般在WEB應用中都採用FILTER技術來處理此類問題,以下類適用於ORACLE,其他數據庫請修改SqlInjectFilter類中的static{ }內的關鍵字。
新增功能: 驗證POST攻擊,精簡驗證列表,優化驗證框架
解決:
1. 配置FILTER,配置位置爲 /WEB-INF/web.xml
<filter>
<filter-name>SqlInjectFilter</filter-name>
<filter-class>SqlInjectFilter</filter-class>
<init-param>
<param-name>enabled</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>failthPath</param-name>
<param-value>/failth.jsp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>SqlInjectFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>SqlInjectFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
說明:
1. <filter-class>SqlInjectFilter</filter-class> 爲類全路徑,可以根據具體情況修改
2. 這裏判定是否啓用,默認是啓用,否則將 <param-value>true</param-value> 修改爲 <param-value>false</param-value>
<init-param>
<param-name>enabled</param-name>
<param-value>true</param-value>
</init-param>
3. 這是判定SQL注入後跳轉的頁面,根據實際情況修改 <param-value>/failth.jsp</param-value> 中的 /failth.jsp
<init-param>
<param-name>failthPath</param-name>
<param-value>/failth.jsp</param-value>
</init-param>
2. 新建FILTER類: SqlInjectFilter ; 2. 配置FILTER
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.util.Vector;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* @ClassName: SqlInjectFilter
* @Description: TODO V2.0
* @author fasake [email protected]
* @date 2012-12-20 下午8:53:34
*
*/
public class SqlInjectFilter implements Filter {
protected Log log = LogFactory.getLog(getClass());
protected static Vector list = new Vector();
protected String enabled = "true";
protected String failthPath = "/TaxWeb/failth.jsp";
public void destroy() {
// TODO Auto-generated method stub
}
public void doFilter(ServletRequest arg0, ServletResponse arg1,
FilterChain arg2) throws IOException, ServletException {
// TODO Auto-generated method stub
HttpServletRequest httpServletRequest = (HttpServletRequest) arg0;
HttpServletResponse httpServletResponse = (HttpServletResponse) arg1;
//獲取GET方法請求參數串
String queryString = httpServletRequest.getQueryString();
//是否啓動,web.xml中配置的初始化參數
if (enabled.equalsIgnoreCase("true")) {
//開始XSS(METHOD:GET)和sql注入檢測
boolean isXssFind = false;
if(queryString!=null && !queryString.equalsIgnoreCase("null")){
String src = java.net.URLDecoder.decode(queryString,"GB2312");
src += "_"+queryString;
if(src!=null && !src.equalsIgnoreCase("null")){
String keyword = "";
for (int i = 0; i < list.size(); i++) {
keyword = list.get(i).toString();
if(src.indexOf(keyword)!=-1){
isXssFind = true;
break;
}
}
if(isXssFind){
log.error("發現疑爲跨站腳本攻擊,檢測判斷請求地址包含非法字符:"+java.net.URLEncoder.encode(keyword,"GB2312"));
httpServletRequest.setAttribute("errorMsg", "發現疑爲跨站腳本攻擊,檢測判斷請求地址包含非法字符:"+java.net.URLEncoder.encode(keyword,"GB2312"));
httpServletRequest.getSession(true).setAttribute("injectKey","發現疑爲跨站腳本攻擊,檢測判斷請求地址包含非法字符:"+java.net.URLEncoder.encode(keyword,"GB2312"));
httpServletRequest.getSession().invalidate();
httpServletResponse.sendRedirect(failthPath);
throw new java.lang.IllegalAccessError();
}
}
}
//開始XSS(METHOD:POST)和sql注入檢測
if(!isXssFind){
arg2.doFilter(new RequestWrapperXSS(httpServletRequest,httpServletResponse,list,failthPath), arg1);
}
}else{
arg2.doFilter(arg0, arg1);
}
}
/**
* 初始化
*/
public void init(FilterConfig arg0) throws ServletException {
// TODO Auto-generated method stub
enabled = arg0.getInitParameter("enabled");
failthPath = arg0.getInitParameter("failthPath");
if (enabled == null || "".equals(enabled)) {
enabled = "true";
}
if (failthPath == null || "".equals(failthPath)) {
enabled = "/TaxWeb/failth.jsp";
}
log.info("SQL注入檢測初始化完成");
}
static {
list.add("'");
list.add("(");
//list.add("or");
//list.add("and");
list.add("[");
list.add("<");
list.add(".."+File.separator);
// 請求參數不能能有腳步
list.add("script");
list.add("+");
//list.add("-");
list.add("*");
list.add("%");
}
}
/**
*
* @ClassName: RequestWrapperXSS
* @Description: TODO 自定義HttpServletRequest
* @author fasake [email protected]
* @date 2012-12-20 下午7:16:00
*
*/
class RequestWrapperXSS extends HttpServletRequestWrapper {
protected Log log = LogFactory.getLog(getClass());
private HttpServletRequest request;
private HttpServletResponse serlvetResponse;
private boolean isXssFind=false;
private Vector keywords;
private String failForwardPath;
public RequestWrapperXSS(HttpServletRequest servletRequest) {
super(servletRequest);
this.request = servletRequest;
}
/**
*
* @param servletRequest
* @param serlvetResponse
*/
public RequestWrapperXSS(HttpServletRequest servletRequest,HttpServletResponse serlvetResponse) {
super(servletRequest);
this.request = servletRequest;
this.serlvetResponse = serlvetResponse;
}
public RequestWrapperXSS(HttpServletRequest servletRequest,HttpServletResponse serlvetResponse,Vector keywords,String failForwardPath) {
super(servletRequest);
this.request = servletRequest;
this.serlvetResponse = serlvetResponse;
this.keywords = keywords;
this.failForwardPath = failForwardPath;
}
/**
* 覆蓋
*/
public String[] getParameterValues(String name){
String[] values = super.getParameterValues(name);
//log.debug("parameter name: "+name+" value:"+values.toString());
try {
xssCheck(values,keywords);
} catch (IllegalAccessError e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw e;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return values;
}
/**
* 覆蓋
*/
public String getParameter(String para){
String postStrInfo = super.getParameter(para);
//log.debug("parameter name: "+para+" value:"+postStrInfo);
try {
//log.debug("parameter name: "+para);
log.debug("(檢測)接收到的["+request.getMethod()+"]請求參數值: " + postStrInfo);
xssCheck(postStrInfo,keywords);
} catch (IllegalAccessError e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw e;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return postStrInfo;
}
public ServletInputStream getInputStream() {
//ServletInputStream
ServletInputStream stream = null;
//POST表單信息
String postStrInfo = null;
try{
stream = request.getInputStream();
byte[] buffer = IOUtils.toByteArray(stream);
postStrInfo = new String(buffer,"GB2312");
//拆分請求參數串
String[] args = postStrInfo.split("\r\n");
for (int i = 0; i < args.length; i++) {
String line = args[i];
//過濾分隔符,和請求參數名稱
if(line.trim().startsWith("-------------------") || line.trim().startsWith("Content-Disposition") || line.trim().equals("")){
log.debug("(忽略)接收到的["+request.getMethod()+"]請求參數值: " + line);
continue;
}
log.debug("(檢測)接收到的["+request.getMethod()+"]請求參數值: " + line);
xssCheck(line,keywords);
}
//驗證完成
final ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
//生成新的ServletInputStream
ServletInputStream sis = new ServletInputStream() {
public int read() throws IOException {
// TODO Auto-generated method stub
return bais.read();
}
};
stream = sis;
}catch(IOException e){
e.printStackTrace();
}
return stream;
}
/**
* @Title: xssCheck
* @Description: TODO 檢測XSS和SQL注入處理類
* @param @param postStrInfo
* @param @throws IOException
* @param @throws IllegalAccessError
* @return void
* @throws
*/
private void xssCheck(String postStrInfo,Vector array) throws IOException,
IllegalAccessError {
if(postStrInfo==null)return;
String src = postStrInfo==null?"null":postStrInfo.toLowerCase();
src = java.net.URLDecoder.decode(src,"GB2312");
//驗證XSS中是否包含相關關鍵字
if(src!=null && !src.equalsIgnoreCase("null")){
String keyword = "";
for (int i = 0; i < array.size(); i++) {
keyword = array.get(i).toString();
if(src.indexOf(keyword)!=-1){
isXssFind = true;
break;
}
}
if(isXssFind){
log.error("發現疑爲跨站腳本攻擊,檢測判斷請求地址包含非法字符:"+java.net.URLEncoder.encode(keyword,"GB2312"));
request.setAttribute("errorMsg", "發現疑爲跨站腳本攻擊,檢測判斷請求地址包含非法字符:"+java.net.URLEncoder.encode(keyword,"GB2312"));
request.getSession(true).setAttribute("injectKey","發現疑爲跨站腳本攻擊,檢測判斷請求地址包含非法字符:"+java.net.URLEncoder.encode(keyword,"GB2312"));
request.getSession().invalidate();
serlvetResponse.sendRedirect(failForwardPath);
throw new java.lang.IllegalAccessError();
}
}
}
/**
*
* @Title: xssCheck
* @Description: TODO 檢測XSS
* @param @param values
* @param @param array
* @param @throws IOException
* @param @throws IllegalAccessError
* @return void
* @throws
*/
private void xssCheck(String[] values,Vector array) throws IOException,IllegalAccessError {
if(values==null){return;}
for (int j = 0; j < values.length; j++) {
String src = java.net.URLDecoder.decode(values[j],"GB2312");
//驗證XSS中是否包含相關關鍵字
if(src!=null && !src.equalsIgnoreCase("null")){
String keyword = "";
for (int i = 0; i < array.size(); i++) {
keyword = array.get(i).toString();
if(src.indexOf(keyword)!=-1){
isXssFind = true;
break;
}
}
if(isXssFind){
log.error("發現疑爲跨站腳本攻擊,檢測判斷請求地址包含非法字符:"+java.net.URLEncoder.encode(keyword,"GB2312"));
request.setAttribute("errorMsg", "發現疑爲跨站腳本攻擊,檢測判斷請求地址包含非法字符:"+java.net.URLEncoder.encode(keyword,"GB2312"));
request.getSession(true).setAttribute("injectKey","發現疑爲跨站腳本攻擊,檢測判斷請求地址包含非法字符:"+java.net.URLEncoder.encode(keyword,"GB2312"));
request.getSession().invalidate();
serlvetResponse.sendRedirect(failForwardPath);
throw new java.lang.IllegalAccessError(); }
}
}
}