场景: 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 dc.liubin@gmail.com * @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 dc.liubin@gmail.com * @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();     }
     }
    }
     }