锁定老帖子 主题:Java应用中的SQL注入攻击和防范
精华帖 (0) :: 良好帖 (3) :: 新手帖 (3) :: 隐藏帖 (12)
|
|
---|---|
作者 | 正文 |
发表时间:2010-04-24
最后修改:2010-04-27
什么叫SQL注入?顾名思义,就是依赖于SQL语句的一种攻击方式,主要采用特殊字符串来处理的SQL漏洞。 这个SQL依赖注入已经是很老的漏洞了,现在基本上DAO层的编写已经很少使用纯JDBC来写sql语句了。 所以在没有使用framework来做DAO,而直接使用JDBC且凭凑的SQL语句的话,那么很容易产生依赖注入的漏洞,如下用户登录模块(目前手头项目中就有活生生的例子) 突破第一个用户名的障碍 登录模块 如: User validUser = login.getUserInfo(user.getName()); 这里的user.getName()是前台从Textfield控件中获得得值,没有做任何处理,于是再看看getUserInfo的方法,如下: public User getUserInfo(String userName) { User validUser = null; String sql = "Select * from WEB_USER where NAME='" + userName + "'"; Database db = null; ResultSet rs = null; try { db = new Database("XXX"); rs = db.execQuery(sql); if (rs != null && rs.next()) { validUser = new User(); .... } } } 我们看到从前台传过来的userName没有经过任何处理而直接凭凑的SQL语句,所以如果输入者精心构造的话,就可以突破第一个屏障,生成一个有效的用户对象,比如: 输入: cjcj' or '1'='1 这样的字符串输入到后台的SQL语句就为: select * from web_user where name='cjcj' or '1'='1' 显然,这个rs肯定是结果集的。我们完成了突破第一个屏障的任务。 细心的人可能注意到,这里我们是用的execQuery,只做一个查询操作,这样并不能达到任意登录的目的,没关系,我们再看。 突破第二层障碍 我们发现: if(retry_cnt >= 3) { login.lockUser(user); List errors = new ArrayList(); ErrorObj error = new ErrorObj(); error.setErrorMsg("登录3次失败,该用户已被锁定,请与管理员联系"); errors.add(error); this.getSession().setAttribute(CTDRConstants.ERROR_ATTRIBUTES, errors); this.getRequest().setAttribute("userName", user.getName()); } 当用户3次输入密码错误的时候,就更改用户状态,用户被锁定(目前很多网站是这种机制,所以需要谨慎一点),既然需要更改用户状态,就有可能涉及到为execUpdate操作,只要有execUpdate操作,hacker们就有机会了,我们继续往下看lockUser方法; public void lockUser(User user) { String sql = "update WEB_USER set status=1 where NAME='" + user.getName() + "'"; Database db = null; try { db = new Database("XXX"); db.execUpdate(sql); } ... } 这里的user对象就是刚从表里select出来的第1个对象,且user.getName()也是直接从前台页面上取的,并没有重新赋值,所以这里可以构造一个拥有语法错误的SQL,让前台报错,从而找到存储用户的表名,比如 输入cjcj' select '1'='1 后台会执行:select * from web_user where name='cjcj' select '1'='1' 然后这条出错的SQL语句信息就有可能暴露在前台了,这样我们就拿到了存储用户信息的表名。 于是我构建一个重置所有密码的SQL语句,如: 输入cjcj' or 1=1;update WEB_USER set passwd=' 这样导致WEB_USER所有密码都为空或者MD5的密码。 最后就可以用cjcj' or '1'='1 这个用户登录了。 但是这样做的缺点是所有用户的密码都更改了很容易被管理员发现的。应该有更好的办法,待续... 另外如果执行的SQL语句为: String sql = "Select * from WEB_USER where NAME='" + userName + "' and PASSWD='"+Passwd+"'"; 这样就更简单了,直接构造上面的那个输入就可以任意登录了。 我们看到,这个应用有待改进的地方: 1、对输入含有'符号进行转换或者禁止; 2、update用户锁定状态时,应该把user.getName()改为: validUser.getName() 3、使用PrepareStatement或者使用dao层框架。 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2010-04-25
老问题了 其实就是把传进来的 参数 都加上 ‘ 参数 ’ 都当字符串了 就得了 应该就不会有问题了吧
|
|
返回顶楼 | |
发表时间:2010-04-25
whaosoft 写道 老问题了 其实就是把传进来的 参数 都加上 ‘ 参数 ’ 都当字符串了 就得了 应该就不会有问题了吧
最佳方案还是在PrepareStatement中使用?来绑定参数,而千万不要用自己拼字符串的形式来生成sql |
|
返回顶楼 | |
发表时间:2010-04-25
最后修改:2010-04-25
补充下:
有些sql语句比如排序的字段是必须用非预编译sql的,采用IBatis的order by $fileld$ ,可以定义一个List<String> 如果传入的字段不在排序字段列的范围之内那么就是非法sql。 另外,对于oracle thin driver来讲,非预编译sql通常比预编译sql要快。 在项目中使用就发现,数据量比较大,sql查询比较复杂,比如单表4000-5000万数据量,非预编译确实快不少,但是在一般情况下即数据量不大,批量查询sql数量不多的情况,还是采用预编译sql,详情请看oracle jdbc driver中的预编译与预编译sql的性能比较一章 |
|
返回顶楼 | |
发表时间:2010-04-25
用面向对象的查询,哪里还存在这样弱智的问题!
|
|
返回顶楼 | |
发表时间:2010-04-25
最后修改:2010-04-25
whaosoft 写道 老问题了 其实就是把传进来的 参数 都加上 ‘ 参数 ’ 都当字符串了 就得了 应该就不会有问题了吧
对参数含有的'进行特殊处理,其实帖子中还说到了一个赋值问题。 erikchang 写道 用面向对象的查询,哪里还存在这样弱智的问题!
**很多项目还是写的JDBC **即便是OO,同样存在,就像楼上仁兄说的IBATIS的$$的情况。 |
|
返回顶楼 | |
发表时间:2010-04-25
哥们OO不是万能的
|
|
返回顶楼 | |
发表时间:2010-04-25
这都什么年代的事情了
|
|
返回顶楼 | |
发表时间:2010-04-25
godfish 写道 这都什么年代的事情了
我们现在的项目就有,看来已经out了~~ |
|
返回顶楼 | |
发表时间:2010-04-25
C_J 写道 ……所以这里可以构造一个拥有语法错误的SQL,让前台报错,从而找到存储用户的表名……
其实很容易就可以让攻击者什么都看不到。 产生一个UUID作为错误ID,和错误时的堆栈信息一起写在日志里,然后在页面上打印,“系统出现错误,错误ID:XXXX……请与管理员联系。” 这样攻击者啥有用的信息都看不到,而你找问题的时候只需要客户提供当时显示的这个ID号码到日志中搜索一下就可以了。 |
|
返回顶楼 | |