SQL注入问题

下述操作将会以MySql数据库为例

引用的是ums库中的t_user表

表中只有一条数据,username与password的数据均为admin

1.简介

SQL注入攻击是比较常见的网络攻击方式之一

它不是利用操作系统的BUG来实现攻击,而是发生于应用程序之数据库层的安全漏洞。

针对程序员编写时的疏忽通过SQL语句,实现无账号登录,甚至篡改数据库

简单来讲,是在输入的字符串之中注入SQL的指令

那么这些注入进去的指令就会被数据库服务器误认为是正常的SQL指令而运行

因此遭到破坏或是入侵。

最常见的就是我们在应用程序中使用字符串联结方式组合 SQL 指令

有心之人就会写一些特殊的符号,恶意篡改原本的 SQL 语法的作用,达到注入攻击的目的。

// 比如验证用户登录需要 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注入问题

MySQL的驱动提供了预编译语句的支持,不同的程序语言,都分别有使用预编译语句的方法

实际上,绑定变量使用预编译语句是预防SQL注入的最佳方式

使用预编译的SQL语句语义不会发生改变

在SQL语句中,变量用问号?表示,黑客即使本事再大,也无法改变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注入问题

我们使用 mybatis 编写 SQL 语句时,难免会使用模糊查询的方法,mybatis 提供了两种方式 #{} 和 ${}

当用户使用${param}进行参数传递时,表示使用拼接字符串的方式进行参数的注入

会将接受到参数的内容不加任何修饰符拼接在 SQL 中,将引起 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();
}

运行后控制台效果如下

 

根据最终展现效果我们可以发现

此时是将参数作为字符串拼接的形式存在 在原本代码中只存在两个查询条件 但是拼接之后最终将会产生四个条件 由于多的两个条件是通过or连接的 最终运行效果相当于select * from t_user

4.MyBatis解决sql注入问题

使用#{param}的方式进行参数传递时

在预处理的时候会把参数部分用一个占位符?替代

其中 param表示接受输入参数的名称。

这样能够有效的解决 SQL 注入问题

从而避免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();
}

运行后控制台效果如下

根据最终展现效果我们可以发现

此时在预处理的时候会把参数部分用一个占位符?替代 传递的参数作为一个整体存在 最终在运行时仍然是原本的两个查询结果 从而避免SQL注入的问题出现

 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值