在java中JDBC中,我们写 SQL 语句的时候,有个预处理功能,这个功能一大优势就是能提高执行速度,尤其是多次操作数据库的情况,再一个优势就是预防SQL注入,严格的说,应该是预防绝大多数的SQL注入。
如下示例代码即为Java中 JDBC 的预处理:
String sql = "select * from t_student where name = ? and content = ?"
try {
PreparedStatement ps = conn.prepareStatement(sql);
ps.setString(1,name);
ps.setString(2,content);
ps.executeUpdate(sql_update);
}catch(Exception e){
e.printStackTrace();
}
那么这个预编译的功能,到底是如何防止 SQL 注入的呢?
其实是因为,SQL语句在代码运行前,已经进行了预编译。在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析,编译和优化,然后对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询。
当运行时,JDBC动态地把参数传给PreparedStatement
时,即使参数里有敏感字符,如: ' or ' 1' = '1
、updatexml(2,concat(0x7e,(version())),0)
等,preparedStatement
会对入参中的关键字进行转义,比如单引号转义成\'
,其流程大致如下:
总之,简单来说,JDBC在处理SQL语句时有一个预编译的过程,而预编译对象就是把一些格式固定的SQL编译后,存放在内存池中即JDBC缓冲池,当我们再次执行相同的SQL语句时就不需要预编译的过程了,所以即使SQL注入特殊的语句,也会只当做参数传进去,不会当做指令执行。