在web项目中,有一个原则是永远不要相信从用户端传过来的不做处理的数据。

比如后端有个登陆接口;

@RequestMapping("/user/login")
    public String login(String name, String password)  {
        String sql="select * from tp_user where name="+name+" and password ="+password;
        //执行sql 得到user
        User user = dao.excute(sql);
        //session或者分发token
        //.....
        return user==null?"登陆失败":"登陆成功";
    }

这是一个很常见的登陆接口,用户会从表单里填入用户名和密码 后端根据用户名和密码去数据库查询 如果有该用户存在就登陆成功。这个逻辑乍一看没问题,但是如果前端填入的数据是这样的

@RequestMapping("/user/login")
    public String login(String name, String password)  {
        name="admin or 1=1 -- ";
        password="1234";
        String sql="select * from tp_user where name="+name+" and password ="+password;

User user = dao.excute(sql);
        return user==null?"登陆失败":"登陆成功";
 }

需要注意的是 此刻拼接而成的sql语句为

select * from tp_user where name=admin or 1=1 -- and password=1234

在sql语言里,--表示注释 会注释掉后面的语句,实际这句话为

select * from tp_user where name=admin or 1=1

这条语句只要表里有数据那就是百分百成功的,这只是伪装了登陆,如果用户传入的是“;drop table xx” 那么执行这条语句的后果可想而知。

针对sql注入,前端解决的方式则为数据格式正则校验,不允许使用特殊符号等等,后端则为预编译和参数化。

预编译的解释为 先预编译sql语句,使他的语义固定,即查询则为查询不允许执行其他操作,参数化则是将传过的值转义作为参数,比如上面的语句

        name="admin or 1=1 -- ";
        password="1234";
        String sql="select * from tp_user where name="+name+" and password ="+password;
        //未参数化
        // "select * from tp_user where name=admin or 1=1 -- and password=1234"
        //参数化后则为
        //  "select * from tp_user where name= 'admin or 1=1 --' and 'password=1234'"

常见的持久层框架mybatis和hibernate中都有避免参数直接拼接语句的方法,比如mybatis中的#{}则是在任何参数上加一个' '。将传入的数据始终当成一个字符串。hibernate自写语句也有对应的占位符 通过修改占位符来参数化避免此问题。

sql注入说起来应该算是一种优点,是数据库保证有权限的用户有100%的掌控。在开发项目中应该对用户的数据进行校验实行白名单制(符合白名单正则的语句才可以执行) 或者使用持久层框架封装好的api。一定要对数据进行校验,不然可能会导致一些未知的错误(击穿,注入。。)。

 

posted on 2020-09-01 14:28  Vinlen  阅读(217)  评论(0编辑  收藏  举报