0、什么是SQL注入
SQL注入发生在应用程序与数据库进行交互的时候的安全漏洞。就是在输入字符串的时候注入SQL指令,在编程时不注意忽略了字符检查,俺么这些注入进去的恶意指令会被数据库服务器误认为正常的SQL指令运行,因此遭受破坏或者入侵。
举个例子
# 正常的sql语句 select * from table_name where user_name = "arvin"; # 如果上述的输入框输入没有拦截,语句就变成了如下形式,多了额外的"or 1=1" 到查询的语句中就是sql注入了。由于1=1永远都是成立的,即where字句总是为真。 select * from table_name where name="arvin" or 1=1; # 实际执行效果 select * from table_name
1、可能出现的数据库
SQL注入可能出现在大部分数据库中,主要支持处理SQL指令的数据库服务器,都有可能受到其手段的攻击。
2、如何防止SQL注入
1、在前端表单进行参数格式控制
2、后台进行参数格式化,过滤是由涉及sql的非法字符
//参考:https://freeman983.iteye.com/blog/1153989 //过滤 ' //ORACLE 注解 -- /**/ //关键字过滤 update,delete static String reg = "(?:')|(?:--)|(/\\*(?:.|[\\n\\r])*?\\*/)|" + "(\\b(select|update|and|or|delete|insert|trancate|char|into|substr|ascii|declare|exec|count|master|into|drop|execute)\\b)"; static Pattern sqlPattern = Pattern.compile(reg, Pattern.CASE_INSENSITIVE);//表示忽略大小写 /*************************************************************************** * 参数校验 * * @param str ep: "or 1=1" */ public static boolean isSqlValid(String str) { Matcher matcher = sqlPattern.matcher(str); if (matcher.find()) { System.out.println("参数存在非法字符,请确认:"+matcher.group());//获取非法字符:or return false; } return true; }
3、持久层使用参数化的持久层
String sql= "select * from user where name=?"; PreparedStatement ps = conn.prepareStatement(sql); ps.setString(1, name); ResultSet rs = ps.executeQuery();
3、框架中防止SQL注入
1、spring中的jdbcTemplate防止SQL注入
1、使用参数化SQL代替字符串拼接(少参)
字符串拼接(不安全): jdbcTemplate.update("update user set age = "+age+" where uuid = "+uuid); 参数化sql(安全): jdbcTemplate.update("update user set age = ? where uuid = ?",new Object[]{age,uuid});
2、参数化,将参数进行数组打包注入(多参)
List<Object> obj = new ArrayList<Object>(); obj.add(name); obj.add(age); String sql = "update user set name=?,age = ? where uuid = 4"; jdbcTemplate.update(sql,obj.toArray());
3、参数化,将参数进行map集合打包,指定参数注入(多参)
Map<String,Object> map = new HashMap<String,Object>(); String sql = "update user set name=:name,age =:age where uuid = 4"; map.put("name",name); map.put("age",age); jdbcTemplate.update(sql,map);
4、参数化,使用编译语句(少参,参数为单体对象)
String sql = "insert into user(name,age) values (?,?)"; jdbcTemplate.update(sql, new PreparedStatementSetter() { @Override public void setValues(PreparedStatement ps) throws SQLException { ps.setString(1, "sixmonth"); ps.setInt(2, 18); }});
5、参数化,使用预编译语句,进行批处理更新(多参,参数为对象集合)
String sql = "insert into user(name,age) values (?,?)"; List<User> userList = new ArrayList<User>();//此处为测试,使用空集合 jdbcTemplate.batchUpdate(sql, new BatchPreparedStatementSetter() { public void setValues(PreparedStatement ps, int i) throws SQLException { ps.setString(1, userList.get(i).getName()); ps.setInt(2, userList.get(i).getAge()); } @Override public int getBatchSize() { return userList.size(); } });
2、mybatis防止SQL注入
mybatis是一款优秀的持久层框架,在防止sql注入方面,mybatis启用了预编译功能,在所有的SQL执行前, 都会先将SQL发送给数据库进行编译,执行时,直接替换占位符"?"即可;
mybatis在处理传参的时候,有两种处理方式,一种是#,另一种是$;
1、#{XXX},将传入参数都当成一个字符串,会自动加双引号,已经经过预编译,安全性高,能很大程度上防止sql注入; 2、${XXX},该方式则会直接将参数嵌入sql语句,未经过预编译,安全性低,无法防止sql注入;一般用于传入数据库对象,例如传入表名。
结论:在编写MyBatis的映射语句时尽量采用#{xxx}这样的格式。若不得不使用${xxx}这样的参数,要手动做好过滤工作,来防止SQL注入。
MyBatis3.0中使用link进行模糊查询,防止sql注入攻击。正确写法如下:
Mysql: select * from t_user where name like concat('%', #{name}, '%')