SQL注入防范

JDBC:

最简单的办法是杜绝SQL拼接,SQL注入攻击能得逞是因为在原有SQL语句中加入了新的逻辑,如果使用PreparedStatement来代替Statement来执行SQL语句,其后只是输入参数,SQL注入攻击手段将无效,这是因为PreparedStatement不允许在不同的插入时间改变查询的逻辑结构

采用预编译语句集,它内置了处理SQL注入的能力,只要使用它的setString方法传值即可:
        String sql= "select * from users where username=? and password=?;
        PreparedStatement preState = conn.prepareStatement(sql);
        preState.setString(1, userName);
        preState.setString(2, password);
        ResultSet rs = preState.executeQuery();

 

在WEB层我们可以过滤用户的输入来防止SQL注入比如用Filter来过滤全局的表单参数 

public class SQLFilter implements Filter {  
private String inj_str = "'|and|exec|insert|select|delete|update|count|*|%
|chr|mid|master|truncate|char|declare|;|or|-|+|,";
17  protected FilterConfig filterConfig = null;  
18  /**
19  * Should a character encoding specified by the client be ignored?
20  */  
21  protected boolean ignore = true;  
22  public void init(FilterConfig config) throws ServletException {  
23  this.filterConfig = config;  
24  this.inj_str = filterConfig.getInitParameter("keywords");  
25  }  
26  public void doFilter(ServletRequest request, ServletResponse response,  
27  FilterChain chain) throws IOException, ServletException {  
28  HttpServletRequest req = (HttpServletRequest)request;  
29  HttpServletResponse res = (HttpServletResponse)response;  
30  Iterator values = req.getParameterMap().values().iterator();//获取所有的表单参数  
31  while(values.hasNext()){  
32  String[] value = (String[])values.next();  
33  for(int i = 0;i < value.length;i++){  
34  if(sql_inj(value[i])){  
35  //TODO这里发现sql注入代码的业务逻辑代码  
36  return;  
37  }  
38  }  
39  }  
40  chain.doFilter(request, response);  
41  }  
42  public boolean sql_inj(String str)  
43  {  
44  String[] inj_stra=inj_str.split("\\|");  
45  for (int i=0 ; i < inj_stra.length ; i++ )  
46  {  
47  if (str.indexOf(" "+inj_stra[i]+" ")>=0)  
48  {  
49  return true;  
50  }  
51  }  
52  return false;  
53  }  
54  } 

 

 也可以单独在需要防范SQL注入的JavaBean的字段上过滤:  
  public static String TransactSQLInjection(String sql) {  
  return sql.replaceAll(".*([';]+|(--)+).*", " ");  
  } 

 

Hibernate防止:

永远也不要写这样的代码:

     String queryString = "from Item i where i.description like '" + searchString + "'";
     List result = session.createQuery(queryString).list();

     如果用户输入:foo' and callSomeStoredProcedure() and 'bar' = 'bar,则你的程序在执行一个简单查询后,还会调用某个存储过程

永远也不要把未经检查的用户输入的值直接传给数据库

如果我们使用参数绑定,还可以提高数据库的执行效率,prepared statement语句被编译一次后,被放在cache中,就不再需要编译,可以提高效率。
参数绑定有2种办法:使用positional parameter或者named parameter。
hibernate支持JDBC样式的positional parameter(查询字符串中使用?),它同使用named parameter的效果一样(查询字符串中使用:)。
使用named parameter,我们重新写上面的查询语句:
String queryString = "from Item item where item.description like :searchString";
 List result = session.createQuery(queryString)
                      .setString("searchString", searchString)
                      .list();

使用positional parameter
String queryString = "from Item item "
                           + "where item.description like ? "
                           + "and item.date > ?";
List result = session.createQuery(queryString)
                  .setString(0, searchString)
                  .setDate(1, minDate)
                  .list();
这段代码可读性强不如上面的强,而且可维护性差

在named parameter中可能有一个参数出现多次的情况,应该怎么处理呢?
String userSearch = "from User u where u.username like :searchString"
                           + " or u.email like :searchString";
List result = session.createQuery(userSearch)
                  .setString("searchString", searchString)
                   .list();

 

 

mybatis防范:

在mybatis中,”${xxx}”这样格式的参数会直接参与sql编译,从而不能避免注入攻击。但涉及到动态表名和列名时,只能使用“${xxx}”这样的参数格式,所以,这样的参数需要我们在代码中手工进行处理来防止注入。

结论:在编写mybatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止sql注入攻击。

“#{xxx}”默认是PreparedStatement的参数注入

 

 

 

其他工具:

apache工具包common-lang中有一个很有用的处理字符串的工具类,其中之一就是StringEscapeUtils,这个工具类是在2.3版本以上加上的去的,利用它能很方便的进行html,xml,Java等的转义与反转义,而且还能对关键字符串进行处理预防SQL注入,不过好像common-lang3.0以后我看着好像没这个处理SQL语句的方法

 

posted @ 2017-05-19 09:31  malcome  阅读(129)  评论(0)    收藏  举报