预防 SQL 注入

什么是 SQL 注入攻击

假设需要按照商品 title 进行筛选数据,MyBatis\src\main\resources\mappers\goods.xml 下的 <mapper namespace="goods">这么写:

    <select id="selectByTitle" parameterType="java.util.Map" resultType="indi.chester.mybatis.entity.Goods">
        <!-- ${} 文本替换, 未经任何处理对SQL文本替换, SQL 注入攻击 -->
        SELECT  * FROM t_goods WHERE  title=${title} LIMIT 5
    <mapper namespace="goods">
        
    </select>

但如果把传入的title 参数换成"'' or 1=1 or 1=''" 串换成这样:

SELECT  * FROM t_goods 
    WHERE  title="'' or 1=1 or 1=''"  LIMIT 5

那么 title 的判别条件就会恒成立,因为内部有一个 "1=1"。 这样一句 SQL 语句就可以把数据库所有数据一次性全部提取出来(如果删掉 LIMIT 5)。这种如熬过条件约束,越权获取数据的方实 这就是 SQL 注入攻击。

解决方法

解决方法也很简单, 那就是将 ${参数名} 换成 #{参数名}, 则会在运行时预编译,将'' or 1=1 or 1='' 看作一整个字符串,而不是运行这个字符串。

<select id="selectByTitle" parameterType="java.util.Map" resultType="indi.chester.mybatis.entity.Goods">
       
        <!-- #{} 预编译传值, 防止 SQL 注入攻击 -->
        SELECT  * FROM  t_goods WHERE  title=#{title} 
 
    </select>

这样如果还是 将 title 参数设置成'' or 1=1 or 1='' 则无法继续访问数据, 除非有一条数据的 title 就是 '' or 1=1 or 1='' .

有的时候需要 ${参数名}

有的时候我们也需要将传入的参数组织成一部分 SQL 语句来运行,而不希望将他看作一个参数。比如我们需要按照 goods_id 降序排序,则需要将 "ORDER BY goods_id DESC" 组织成一段 SQL 语句,而不是当作一个参数。MyBatis\src\main\resources\mappers\goods.xml 下的 <mapper namespace="goods">这么写:

    <select id="selectByTitle" parameterType="java.util.Map" resultType="indi.chester.mybatis.entity.Goods">
 
        <!-- 有的时候 使用原文 -->
        SELECT  * FROM  t_goods WHERE  title=#{title} ${order}
    </select>

在 MyBatis\src\test\java\MyBatisTest.java 添加下面这个方法:

    @Test
    public void testSelectByTitle() throws Exception{
        SqlSession sqlSession=null;
        try {
            sqlSession=MyBatisUtils.openSession();
            Map parm = new HashMap();
            //尝试 SQL 注入攻击
            //parm.put("title", "'' or 1=1 or 1=''");
            
            //有的时候 使用原文
            parm.put("title", "亲润 孕妇护肤品豆乳大米盈润保湿胶原蚕丝面膜(18片装)");
            parm.put("order", "ORDER BY goods_id DESC");
            List<Goods> goodsList= sqlSession.selectList("goods.selectByTitle", parm);
            for (Goods g: goodsList){
                System.out.println(g.getGoodsId() +" " + g.getTitle());
            }

        }catch (Exception e){
            if (sqlSession!=null){
                sqlSession.rollback();
            }
            throw e;
        }finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }

运行结果:

21:25:14:725 [main]  DEBUG goods.selectByTitle -==>  Preparing: SELECT * FROM t_goods WHERE title=? ORDER BY goods_id DESC  
21:25:14:755 [main]  DEBUG goods.selectByTitle -==> Parameters: 亲润 孕妇护肤品豆乳大米盈润保湿胶原蚕丝面膜(18片装)(String) 
21:25:14:784 [main]  DEBUG goods.selectByTitle -<==      Total: 3 
1287 亲润 孕妇护肤品豆乳大米盈润保湿胶原蚕丝面膜(18片装)
1265 亲润 孕妇护肤品豆乳大米盈润保湿胶原蚕丝面膜(18片装)
739 亲润 孕妇护肤品豆乳大米盈润保湿胶原蚕丝面膜(18片装)

Last updated

Was this helpful?