JDBC连接如何防⽌SQL注⼊?
1. 绪⾔
想要学习mybatis的相关知识,学习之前复习了下JDBC的相关知识
加快新陈代谢
发现⾃⼰竟然连如何实现JDBC连接都不知道了,真的是少壮⽤CV(粘贴复制),⽼⼤徒伤悲 总结⼀下,JDBC连接分为三步:
1. 加载JDBC驱动
2. 创建数据库连接
3. 操作数据库实现增删改查:获取statement,执⾏SQL语句,处理执⾏结果
简单的连接和查询⽰例如下:
import java.sql.*;
87版红楼梦演员现状public class Test {
private static final String DRIVER ="sql.jdbc.Driver";
// 指定编码格式,解决⽆法匹配中⽂字符的问题
private static final String URL ="jdbc:mysql://localhost:3306/lucy?useUnicode=TRUE&characterEncoding=UTF-8"; private static final String USER ="root";
private static final String PASSWORD ="123456";房产过户手续>自酿葡萄酒的方法
private static Connection conn = null;
private static Statement stmt = null;
private static ResultSet rs = null;
private static PreparedStatement ptmt = null;
public static void main(String[] args){
try{
// 1. 加载驱动
Class.forName(DRIVER);
// 2. 创建数据库连接
conn = Connection(URL, USER, PASSWORD);
// 3. 操作数据库,实现增删改查
stmt = ateStatement();
rs = uteQuery("select * from stu");
// 遍历查询结果
()){
StringBuilder sb =new StringBuilder("[id=");
sb.Long("id"));
sb.append(", name="+ rs.getString("name"));
剑魂怎么加点
sb.append(", age="+ rs.getInt("age"));
sb.append(", class_id="+ rs.getLong("class_id"));
sb.append("]");
System.out.String());
}
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch(SQLException throwables){
throwables.printStackTrace();
}finally{// 关闭连接
try{
if(rs != null){
rs.close();
}
if(stmt != null){
stmt.close();
}
if(ptmt != null){
ptmt.close();
}
if(conn != null){
conn.close();
}
}catch(SQLException throwables){
throwables.printStackTrace();
}
}
}
}
stu表的建表语句见博客:
2. SQL注⼊的问题
字符串拼接引发SQL注⼊
新需求:这是⼀个简单的学⽣信息管理系统,需要通过学⽣姓名查询学⽣信息。
详细设计:编写⼀个⽅法,⼊参为学⽣姓名,将⼊参拼接成完整的SQL语句
代码实现:
public static void queryWithParam(String name)throws SQLException {
String sql ="select * from stu where name='"+ name +"'";
rs = uteQuery(sql);
// 打印查询结果
()){
StringBuilder sb =new StringBuilder("[id=");
sb.Long("id"));
sb.append(", name="+ rs.getString("name"));
sb.append(", age="+ rs.getInt("age"));
sb.append(", class_id="+ rs.getLong("class_id"));
sb.append("]");
System.out.String());
}
}
如果⽤户传⼊的参数为lucy,此时SQL字符串为select * from stu where name='lucy',能查询到想要的数据
如果这是⼀个急性⼦的⽤户,他不仅想要知道lucy的信息,还想要知道郭麒麟的信息,这时传⼊的参数为:lucy' or name='郭麒麟拼接出来的SQL语句为select * from stu where name='lucy' or name='郭麒麟',查询出来的学⽣信息就不⽌lucy⼀条信息了
由此可见,这样的字符串拼接⽅式存在SQL注⼊的风险
②字符串转义避免SQL注⼊
上⾯的例⼦会可能发⽣SQL注⼊,那是因为传⼊的字符串在特定的环境下它不再是⼀个整体,⽽是可以拆分出多个查询条件
为了避免SQL注⼊,最直接的想法就是让传⼊的参数始终是⼀个整体。
我们可以对⼊参中的'字符进⾏转义\',这样就保证其整体性
实践发现,lucy\' or name=\'郭麒麟简单的转义在字符串拼接时,会⾃动取消转义:select * from stu where name='lucy' or name='郭麒麟'因此,拼接⽽成的SQL语句任然有SQL注⼊的风险
廖雪峰官⽹:
要避免SQL注⼊攻击,⼀个办法是针对所有字符串参数进⾏转义,但是转义很⿇烦,⽽且需要在任何使⽤SQL的地⽅增加转义代码。
看起来,通过转义避免SQL注⼊是很不现实的
③使⽤PreparedStatement避免SQL注⼊
可以使⽤PreparedStatement来实现数据库操作,它的特点是:使⽤占位符?表⽰SQL语句中的参数,通过set⽅法为SQL语句传⼊参数简单⽰例如下:
public static void queryPrepare(String name)throws SQLException {
/
/ 预编译SQL,避免
ptmt = conn.prepareStatement("select * from stu where name=?");
ptmt.setString(1, name);
// 打印预编译后的SQL语句胡月简历
System.out.String());
rs = uteQuery();
()){
StringBuilder sb =new StringBuilder("[id=");
sb.Long("id"));
sb.append(", name="+ rs.getString("name"));
sb.append(", age="+ rs.getInt("age"));
sb.append(", class_id="+ rs.getLong("class_id"));
sb.append("]");
System.out.String());
}
}
这⾥,我们仍然传⼊之前成功发⽣SQL注⼊的参数lucy' or name='郭麒麟,发现打印出来的SQL语句为:select*from stu where name='lucy\' or name=\'郭麒麟'
神奇之处:预编译⾃动将SQL语句进⾏了转义,使得传⼊的参数成为了⼀个整体
也就是说,这⾥的查询条件变成了lucy\' or name=\'郭麒麟,SQL语句⽆法被截断了
总结: 使⽤PreparedStatement的优点:
1. 安全性不同: PreparedStatement可以有效防⽌sql注⼊,⽽Statment不能防⽌sql注⼊。
2. 语法不同:PreparedStatement可以使⽤预编译的sql,⽽Statment只能使⽤静态的sql
3. 效率不同:对于更改参数的同⼀SQL语句,PreparedStatement可以使⽤sql缓存区,效率⽐Statment⾼