首页 > 技术文章 > 防止sql注入

duaner92 2019-06-27 16:22 原文

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注入

推荐阅读