sql注入是一种十分简单的对数据库数据进行攻击的一种手段,如下所示:
private String getNameByUserId(String userId) {
Connection conn = getConn();//获得连接
String sql = "select name from user where id=" + userId;
PreparedStatement pstmt = conn.prepareStatement(sql);
ResultSet rs=pstmt.executeUpdate();
......
}
当传入的参数为:"3;drop table user;"时,
执行时的语句就是:select name from user where id=3;drop table user;
这对于数据来说是非常危险的。
如何避免sql注入呢?
1:遵循编程规范
首先执行预编译,随后再填写参数,这样参数会替换掉编译好的语句中的?占位符,最后执行完整的sql语句。
例如:
Connection conn = getConn();//获得连接
String sql = "select name from user where id= ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1, userId);
ResultSet rs=pstmt.executeUpdate();
......
还是上边的参数:"3;drop table user;"
最后执行语句就会是:select name from user where id="3;drop table user;"。 避免了sql注入。
2:使用存储过程
存储过程(Stored Procedure)是一组完成特定功能的SQL语句集,经编译后存储在数据库中,用户通过调用存储过程并给定参数(如果该存储过程带有参数)就可以执行它,也可以避免SQL注入攻击
例如:
Connection conn = getConn();
stmt = conn.prepareCall("{call name_from_user(?,?)}");
stmt.setInt(1,2);
stmt.registerOutParameter(2, Types.VARCHAR);
stmt.execute();
String name= stmt.getString(2);
存储过程如下:
use user;
delimiter //
create procedure name_from_user(in user_id int,out user_name varchar(20))
begin
select name into user_name from user where id=user_id;
end
//
delimiter ;
3:mybatis半自动持久化框架,其底层原理也是预编译,参数替换占位符?
注意:
在编写MyBatis的映射语句时,尽量采用“#{xxx}”这样的格式。若不得不使用“${xxx}”这样的参数,要手工地做好过滤工作,来防止SQL注入攻击。
#{}:相当于JDBC中的PreparedStatement
${}:是输出变量的值
简单说,#{}是经过预编译的,是安全的;${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在SQL注入