SQL注入的理解及预防----MySQL
程序猿(媛)大神的话可以略过,不过有兴趣看到的话希望可以指点一二,在此谢过。技术小白可以加深印象的文章!
现在不管什么网站,基本都是有用户的,必然要输入用户名和密码来登陆一些网站。那如果用户会一些编程的知识,有时候会“皮一下”,可能会对网站做一些攻击,例如SQL注入,XSS跨站(跨站脚本攻击),CSRF(跨站伪造请求攻击),COOKIE欺骗,DDOS攻击(分布式拒绝服务攻击),文件上传漏洞攻击等等,现在先讨论SQL注入攻击,下次再讨论其他攻击的方式!
先来说说什么是SQL注入:
SQL注入是一种比较常见的网络攻击方式之一,它是由程序员开发过程中代码不严谨(没有过滤敏感字符,绑定变量),导致攻击者可以通过sql语法,不择手段,而让其实现无账号登陆的效果,甚至可以对数据库进行篡改!
以PHP+MySQL做的web站点为技术栈的前提,首先有数据库,而且其中一张表为用户的信息表,基本的字段(本文只说有用到的字段,且有数据)编号,用户名,密码等字段;
<?Php
if($_POST){
$link = mysql_connect("主机的ip", "数据库用户名", "数据库密码");
mysql_select_db('demo', $link);
$uname = empty($_POST['username']) ? '' : $_POST['username'];
$password = empty($_POST['password']) ? '' : $_POST['password'];
$md5password = md5($password);
$sql = "SELECT uid,username FROM user WHERE username='{$uname}' AND password='{$md5password}'";
$query = mysql_query($sql, $link);
$userinfo = mysql_fetch_array($query, MYSQL_ASSOC);
if(!empty($userinfo)){
//登录成功,打印出会员信息
echo '<pre>',print_r($userinfo, 1),'</pre>';
} else {
echo "用户名不存在或密码错误!";
}
}
这是login.php后台处理业务伪码
<form name="login" method="post" action="">
登录帐号: <input type="text" name="username" value="" size=30 /><br /><br />
登录密码: <input type="text" name="password" value="" size=30 /><br /><br />
<input type="submit" value="登录" />
</form>
这是前台的form表单
上面如果正确输入数据库中的用户名和密码,则可以正常登陆,而且login.php的sql语句可以写为:
SELECT id,username FROM user WHERE username='zhangsan' AND password='密码'(当然这块的密码是加密后的)
但是如果攻击者则可能会在用户名中输入:zhangsan' AND 1=1-- hack,密码随便输入即可,这样在form表单提交之后的sql语句处理为:
SELECT id,username FROM user WHERE username='zhangsan'AND 1=1-- hack AND password='密码'
然后在数据库中执行sql语句,1=1是成立的条件,所以就是只要攻击者随便注册一个账号或者知道某个用户名,无需密码就可以登录到系统中。
那我们如何防御sql注入呢?
在服务器的那方面,应该保证上线的项目中错误提示的信息应该是关闭的(不应该是调试的模式)。例如PHP的生产环境中,应该在php.ini中把display_errors设置为Off,这样就关不了错误提示。
1、开发人员在开发过程中要检查变量数据类型和格式,确保用户输入的内容是我们预想的格式来输入,其中可以用正则去验证。
2、对于无法确定格式的变量,对一些特殊符号过滤或者转义进行处理。PHP的话可以PHP自带过滤和转义的函数进行处理。Eg:addslashes(单双引号、反斜线及NULL加上反斜线转义)
3、还可以绑定变量,使用预编译语句的方式进行处理sql注入,因为MySQL的mysqli驱动提供了预编译语句的支持,这里以PHP语言为例:
if($username){
//使用mysqli驱动连接demo数据库
$mysqli = new mysqli("主机ip", "数据库用户名", "数据库密码", 'demo');
//使用问号替代变量位置
$sql = "SELECT id,username FROM user WHERE username=?";
$stmt = $mysqli->prepare($sql);
//绑定变量
$stmt->bind_param("s", $uname);
$stmt->execute();
$stmt->bind_result($id, $uname);
while ($stmt->fetch()) {
$row = array();
$row['id'] = $id;
$row['username'] = $uname;
$userinfo[] = $row;
}
}
这样在代码提交的时候,在sql语句中变量用问号表示,就无法改变sql语句的结构,就算是传递 zhangsan'AND 1=1-- hack 参数,只会当做字符串来解释查询,可以说是从根本上解决的sql注入的攻击。
而实际上,绑定变量使用预编译也是预防sql注入的最好方式。
所以开发人员在实际项目过程中,一定要做好代码的严谨,而且永远不要相信用户那边的输入,最好对其格式上有严格的要求。