1.简介
// 比如验证用户登录需要 username 和 password,编写的 SQL 语句如下:
select * from t_user where (username = '"+ username +"') and (password = '"+ password +"');
// username 和 password 字段被恶意填入
username = "1' OR '1'='1";
password = "1' OR '1'='1";
// 将导致原本的 SQL 字符串被填为:
select * from t_user where (username = '1' or '1'='1') and (password = '1' or '1'='1');
// 此时实际上运行的sql为
select * from t_user;
// 也就是不再需要 username 和 password 账密即达到登录的目的,结果不言而喻。
public static void main(String[] args) {
// 此时真实数据库中只有一个用户,username与password均为admin
login("admin","admin"); //登录成功
login("admin","111"); // 登录失败
login("1' or '1' = '1","1' or '1' = '1"); //登录成功
}
public static void login(String username,String password){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String url = "jdbc:mysql://127.0.0.1:3306/ums?useUnicode=true&characterEncoding=utf-8";
User user = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url,"root","");
String sql = new StringBuffer()
.append(" select id,username,password,phone,address ")
.append(" from t_user ")
.append(" where username = '"+ username +"' ")
.append(" and password = '"+ password +"' ")
.toString();
ps = conn.prepareStatement(sql);
rs = ps.executeQuery();
while(rs.next()){
System.out.println("登录成功");
return;
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("登录失败");
}
2.解决sql注入问题
public static void main(String[] args) {
login("admin","admin"); //登录成功
login("admin","111"); // 登录失败
login("'1' or '1' = '1'","'1' or '1' = '1'"); // 登录失败
}
public static void login(String username,String password){
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
String url = "jdbc:mysql://127.0.0.1:3306/ums?useUnicode=true&characterEncoding=utf-8";
User user = null;
try {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url,"root","");
String sql = new StringBuffer()
.append(" select id,username,password,phone,address ")
.append(" from t_user ")
.append(" where username = ? ")
.append(" and password = ? ")
.toString();
ps = conn.prepareStatement(sql);
ps.setString(1,username);
ps.setString(2,password);
rs = ps.executeQuery();
while(rs.next()){
System.out.println("登录成功");
return;
}
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("登录失败");
}
3.MyBatis中的Sql注入问题
<!-- sql注入问题 -->
<select id="selectByUsernameAndPassword4" resultType="user">
select <include refid="userColumn"></include>
from t_user
where username = ${username}
and password = ${password}
</select>
// 在遇到sql注入后,相当于查询了所有
public List<User> selectByUsernameAndPassword4(@Param("username") String username, @Param("password") String password);
// 测试
SqlSession session = null;
try {
session = MyBatisUtil.getSession();
UserDao userDao = session.getMapper(UserDao.class);
List<User> users = userDao.selectByUsernameAndPassword4("'1' or '1'='1'","'1' or '1'='1'");
// 此时查询了所有的t_user表中的数据
// 可以将此时的sql语句看做:select * from user
System.out.println(users);
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
} finally {
MyBatisUtil.close();
}
运行后控制台效果如下
根据最终展现效果我们可以发现
4.MyBatis解决sql注入问题
<!-- 避免sql注入 -->
<select id="selectByUsernameAndPassword5" resultType="user">
select <include refid="userColumn"></include>
from t_user
where username = #{username}
and password = #{password}
</select>
SqlSession session = null;
try {
session = MyBatisUtil.getSession();
UserDao userDao = session.getMapper(UserDao.class);
List<User> users = userDao.selectByUsernameAndPassword5("'1' or '1'='1'","'1' or '1'='1'");
// 此时将传递的参数整体作为一个字符串
// 即username的值为:'1' or '1'='1'
// password的值为:'1' or '1'='1'
// 因此没有查询到对应的数据
session.commit();
} catch (Exception e) {
e.printStackTrace();
session.rollback();
} finally {
MyBatisUtil.close();
}
运行后控制台效果如下
根据最终展现效果我们可以发现