此时如果输入正确的用户名 plhwin 和密码 123456,执行的SQL语句为:
SELECT uid,username FROM user WHERE username='plhwin' AND password='e10adc3949ba59abbe56e057f20f883e' 上面语句没有任何问题,可以看到页面打印出了登录成功后的会员信息,但如果有捣蛋鬼输入的用户名为 plhwin' AND 1=1-- hack,密码随意输入,比如aaaaaa,那么拼接之后的SQL查询语句就变成了如下内容:
SELECT uid,username FROM user WHERE username='plhwin' AND 1=1-- hack' AND password='0b4e7a0e5fe84ad35fb5f95b9ceeac79' 执行上面的SQL语句,因为1=1是永远成立的条件,这意味着***只需要知道别人的会员名,无需知道密码就能顺利登录到系统。 如何确定SQL注入漏洞
通过以上的实例,我们仍然还会有疑问:***并不知道我们程序代码的逻辑和SQL语句的写法,他是如何确定一个网站是否存在SQL注入漏洞呢?一般说来有以下2种途径: 1、错误提示
如果目标Web网站开启了错误显示,***者就可以通过反复调整发送的参数、查看页面打印的错误信息,推测出Web网站使用的数据库和开发语言等重要信息。 2、盲注
除非运维人员疏忽,否则大部分的Web运营网站应该都关闭了错误提示信息,此时***者一般会采用盲注的技巧来进行反复的尝试判断。 仍然以上面的数据表user为例,我们之前的查看会员详情页面的url地址为userinfo.php?username=plhwin,此时***分别访问userinfo.php?username=plhwin' AND 1=1-- hack和userinfo.php?username=plhwin' AND 1=2-- hack,如果前者访问能返回正常的信息而后者不能,就基本可以判断此网站存在SQL注入漏洞,因为后者的1=2这个表达式永远不成立,所以即使username传入了正确的参数也无法通过,由此可以推断这个页面存在SQL注入漏洞,并且可以通过username参数进行注入。 如何防御SQL注入
对于服务器配置层面的防范,应该保证生产环境的Webserver是关闭错误信息的,比如PHP在生产环境的配置文件php.ini中的display_errors应该设置为Off,这样就关闭了错误提示,下面我们更多的从编码的角度来看看如何防范SQL注入。
上面用两个实例分析了SQL注入***的技巧,可以看到,但凡有SQL注入漏洞的程序,都是因为程序要接受来自客户端用户输入的变量或URL传递的参数,并且这个变量或参数是组成SQL语句的一部分,对于用户输入的内容或传递的参数,我们应该要时刻保持警惕,这是安全领域里的「外部数据不可信任」的原则,纵观Web安全领域的各种***方式,大多数都是因为开发者违反了这个原则而导致的,所以自然能想到的,就是从变量的检测、过滤、验证下手,确保变量是开发者所预想的。 1、检查变量数据类型和格式
$sql = "SELECT uid,username FROM user WHERE uid='{$uid}'";
以及
$uid = isset($_GET['uid']) ? $_GET['uid'] : 0;
$uid = addslashes(uid);
$sql = "SELECT uid,username FROM user WHERE uid={$uid}";
对于PHP程序+MySQL构架的程序,在动态的SQL语句中,使用单引号把变量包含起来配合addslashes函数是应对SQL注入***的有效手段,但这做的还不够,像上面的2条SQL语句,根据「检查数据类型」的原则,uid都应该经过intval函数格式为int型,这样不仅能有效避免第二条语句的SQL注入漏洞,还能使得程序看起来更自然,尤其是在NoSQL(如MongoDB)中,变量类型一定要与字段类型相匹配才可以。上面两个查询语句都经过了php的addslashes函数过滤转义,但在安全性上却大不相同,在MySQL中,对于int类型字段的条件查询,上面个语句的查询效果完全一样,由于第一句SQL的变量被单引号包含起来,SQL注入的时候,***面临的首要问题是必须要先闭合前面的单引号,这样才能使后面的语句作为SQL执行,并且还要注释掉原SQL语句中的后面的单引号,这样才可以成功注入,由于代码里使用了addslashes函数,***的***会无从下手,但第二句没有用引号包含变量,那***也不用考虑去闭合、注释,所以即便同样采用addslashes转义,也还是存在SQL***漏洞。
从上面可以看出,第二个SQL语句是有漏洞的,不过由于使用了addslashes函数,你会发现***的***语句也存在不能使用特殊符号的条件限制,类似where username='plhwin'这样的***语句是没法执行的,但是***可以将字符串转为16进制编码数据或使用char函数进行转化,同样能达到相同的目的,如果对这部分内容感兴趣,可以点击这里查看。而且由于SQL保留关键字,如「HAVING」、「ORDER BY」的存在,即使是基于黑白名单的过滤方法仍然会有或多或少问题,那么是否还有其他方法来防御SQL注入呢? 3、绑定变量,使用预编译语句
MySQL的mysqli驱动提供了预编译语句的支持,不同的程序语言,都分别有使用预编译语句的方法,我们这里仍然以PHP为例,编写userinfo2.php代码: