1.简述
1.1.什么是SQL注入
SQL注入即是指web应用程序对用户输入数据的合法性没有判断或过滤不严,攻击者可以在web应用程序中事先定义好的查询语句的结尾上添加额外的SQL语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器执行非授权的任意查询,从而进一步得到相应的数据信息。
2.SQL注入案例(Jdbc)
2.1.建表语句
create table user_mybatis( id int Primary key, username varchar(30), password varchar(30) ); insert into user_table values(1,user -1','12345'); insert into user_table values(2,user-2','12345'); |
2.2.使用Jdbc加载sql文
package com.me.homesickness.mybatis.test; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import com.mysql.jdbc.Connection; import com.mysql.jdbc.PreparedStatement; /** * sql注入Jdbc * @author Administrator * */ public class JdbcSqlInjection { public static void main(String[] args) throws ClassNotFoundException, SQLException { String userName = "zhangsan"; String password = "hdhdfbhgs+++jse"; String sql = "SELECT id,username from user_mybatis WHERE " + "username='" + userName + "' AND " + "password='" + password + "'";
// 1.加载驱动 Class.forName("com.mysql.jdbc.Driver");
// 2.创建数据库连接对象 Connection conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql?characterEncoding=utf8", "root", "mnj852123");
// 3.指定查询sql PreparedStatement stat = (PreparedStatement) conn.prepareStatement(sql); System.out.println(stat.toString());
// 4.执行查询,获取结果集 ResultSet rs = stat.executeQuery();
// 5.输出查询内容 while(rs.next()) { String id = rs.getString(1); String name = rs.getString(2); System.out.println("id:" + id + " name:" + name); } } } |
2.3.执行结果
2.4.修改username的值
// String userName = "zhangsan"; String userName = "' OR 1=1 -- '"; |
解析:
1)“--” 表示SQL注释,因此后面语句忽略;
2)因为1=1恒成立,因此 username='' OR 1=1 恒成立,因此SQL语句等同于:
SELECT id,username from user_mybatis WHERE username='' OR 1=1 -- '' AND password='hdhdfbhgs+++jse' |
2.5.执行代码
package com.me.homesickness.mybatis.test; import java.sql.DriverManager; import java.sql.ResultSet; import java.sql.SQLException; import com.mysql.jdbc.Connection; import com.mysql.jdbc.PreparedStatement; /** * sql注入Jdbc * @author Administrator * */ public class JdbcSqlInjection { public static void main(String[] args) throws ClassNotFoundException, SQLException { // String userName = "zhangsan"; String userName = "' OR 1=1 -- '"; String password = "hdhdfbhgs+++jse"; String sql = "SELECT id,username from user_mybatis WHERE " + "username='" + userName + "' AND " + "password='" + password + "'";
// 1.加载驱动 Class.forName("com.mysql.jdbc.Driver");
// 2.创建数据库连接对象 Connection conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql?characterEncoding=utf8", "root", "mnj852123");
// 3.指定查询sql PreparedStatement stat = (PreparedStatement) conn.prepareStatement(sql); System.out.println(stat.toString());
// 4.执行查询,获取结果集 ResultSet rs = stat.executeQuery();
// 5.输出查询内容 while(rs.next()) { String id = rs.getString(1); String name = rs.getString(2); System.out.println("id:" + id + " name:" + name); } } } |
2.6.解决方法
/** * JDBC解决SQL注入 */ String userName = "' OR 1=1 -- '"; String password = "hdhdfbhgs+++jse"; String sql = "SELECT id,username FROM user_mybatis WHERE username = ? AND password = ?";
// 1.加载驱动 Class.forName("com.mysql.jdbc.Driver");
// 2.创建数据库连接对象 Connection conn = (Connection) DriverManager.getConnection("jdbc:mysql://localhost:3306/mysql?characterEncoding=utf8", "root", "mnj852123");
// 3.指定查询sql PreparedStatement stat = (PreparedStatement) conn.prepareStatement(sql); stat.setString(1, userName); stat.setString(2, password); System.out.println(stat.toString());
// 4.执行查询,获取结果集 ResultSet rs = stat.executeQuery();
// 5.输出查询内容 while(rs.next()) { String id = rs.getString(1); String name = rs.getString(2); System.out.println("id:" + id + " name:" + name); } |
2.7.执行结果
3.Mybatis中符号#与符号$的区别
动态 sql 是 mybatis 的主要特性之一,在 mapper 中定义的参数传到 xml 中之后,在查询之前 mybatis 会对其进行动态解析。mybatis 为我们提供了两种支持动态 sql 的语法:#{} 以及 ${}。
在下面的语句中,如果 username 的值为 zhangsan,则两种方式无任何区别:
select * from user where name = #{name};
select * from user where name = ${name};
其解析之后的结果均为
select * from user where name = 'zhangsan';
但是 #{} 和 ${} 在预编译中的处理是不一样的。#{} 在预处理时,会把参数部分用一个占位符 ? 代替,变成如下的 sql 语句:
select * from user where name = ?;
而 ${} 则只是简单的字符串替换,在动态解析阶段,该 sql 语句会被解析成
select * from user where name = 'zhangsan';
以上,#{} 的参数替换是发生在 DBMS 中,而 ${} 则发生在动态解析过程中。
那么,在使用过程中我们应该使用哪种方式呢?
答案是,优先使用 #{}。因为 ${} 会导致 sql 注入的问题。看下面的例子:
select * from ${tableName} where name = #{name}
在这个例子中,如果表名为
user; delete user; --
则动态解析之后 sql 如下:
select * from user; delete user; -- where name = ?;
--之后的语句被注释掉,而原本查询用户的语句变成了查询所有用户信息+删除用户表的语句,会对数据库造成重大损伤,极大可能导致服务器宕机。
因为数据库的原因导致表名用参数传递进来的时候,只能使用 ${},所以我们在这种用法中要小心sql注入的问。
4.SQL注入案例(Mybatis)
4.1.建表语句
create table user_mybatis( id int Primary key, username varchar(30), password varchar(30) ); insert into user_table values(1,user -1','12345'); insert into user_table values(2,user-2','12345'); |
4.2.创建POJO类
package com.me.homesickness.mybatis.pojo; public class UserMybatis {
private int id; private String userName; private String passWord;
public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getPassWord() { return passWord; } public void setPassWord(String passWord) { this.passWord = passWord; } } |
4.3.创建Mapper类
package com.me.homesickness.mybatis.mapper; import com.me.homesickness.mybatis.pojo.UserMybatis; public interface UserMybatisMapper { public UserMybatis login(UserMybatis userMybatis);
} |
4.4.创建Mapper映射XML文件UserMybatisMapper.xml
<?xml version="1.0" encoding="UTF-8" ?> <!-- 定义文档类型为mybatis的sql映射文件 --> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.me.homesickness.mybatis.mapper.UserMybatisMapper"> <!-- SQL注入测试 --> <select id="login" parameterType="com.me.homesickness.mybatis.pojo.UserMybatis" resultType="com.me.homesickness.mybatis.pojo.UserMybatis"> SELECT id ,username as userName FROM user_mybatis WHERE username='${userName}' AND password='${passWord}' </select> </mapper> |
4.5.测试SQL注入
package com.me.homesickness.mybatis.test; import java.io.IOException; import java.io.Reader; import java.util.List; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.me.homesickness.mybatis.pojo.UserMybatis; /** * Mybatis的SQL注入 * @author Administrator * */ public class MybatisSqlInjection { public static void main(String[] args) throws IOException { String resource = "config/SqlMapConfig.xml"; String userName = "' OR 1=1 -- '"; //String userName = "zhangsan"; String password = "hdhdfbhgs+++jse";
// 1.读取配置文件 Reader reader = Resources.getResourceAsReader(resource);
// 2.获取会话工厂 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); // 3.从会话工厂里获取SqlSession对象 SqlSession openSession = sqlSessionFactory.openSession();
// 4.指定查询语句 String sql = "com.me.homesickness.mybatis.mapper.UserMybatisMapper.login";
// 5.调用api查询 UserMybatis userMybatis = new UserMybatis(); userMybatis.setUserName(userName); userMybatis.setPassWord(password); List<UserMybatis> listUserMybatis = openSession.selectList(sql, userMybatis); listUserMybatis.forEach(user-> { System.out.println(user.getUserName()); }); } } |
4.6.执行结果
4.7.解决sql注入,在mapper.xml文件中将符号${}替换为符号#{}
<?xml version="1.0" encoding="UTF-8" ?> <!-- 定义文档类型为mybatis的sql映射文件 --> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.me.homesickness.mybatis.mapper.UserMybatisMapper"> <!-- SQL注入测试 --> <!-- <select id="login" parameterType="com.me.homesickness.mybatis.pojo.UserMybatis" resultType="com.me.homesickness.mybatis.pojo.UserMybatis"> SELECT id ,username as userName FROM user_mybatis WHERE username='#{userName}' AND password='#{passWord}' </select> --> <select id="login" parameterType="com.me.homesickness.mybatis.pojo.UserMybatis" resultType="com.me.homesickness.mybatis.pojo.UserMybatis"> SELECT id ,username as userName FROM user_mybatis WHERE username=#{userName} AND password=#{passWord} </select> </mapper> |
4.8.执行结果
4.9.总结
1)优先使用 #{}。因为 ${} 会导致 sql 注入的问题
2)#{}被符号”’”包裹时会报错,使用时需要去掉符号"'"
参照:
https://baike.baidu.com/item/sql%E6%B3%A8%E5%85%A5/150289?fr=aladdin