飞道的博客

1-Web安全——初识SQL注入漏洞

338人阅读  评论(0)

1. 什么是SQL注入

SQL注入是web应用程序对用户输入的数据没有严格进行合法校验和过滤,导致前端传入到后端的数据被攻击者恶意修改,欺骗服务器执行SQL语句实现对数据库的任意操作,这就是SQL注入漏洞的原理。

 

2. 为什么会有SQL注入

  1. 研发人员在开发过程中代码逻辑不严谨造成的,例如没有对SQL参数进行严格过滤
  2. 未启用框架的安全配置,例如PHP的magic_quotes_gpc
  3. 未启防火墙等其他安全防护设备

 

3. SQL注入的种类

SQL注入的种类主要有以下几种:

  1. 联合注入
  2. 布尔注入
  3. 延时注入
  4. 报错注入
  5. insert注入

除了以上几种,还有很多其他的SQL注入方式,后面会对这些注入方式进行详细介绍。

 

4. SQL注入原理

现在通过一个用户登录的万能密码漏洞来分析SQL注入漏洞产生的原因,测试环境如下:

 

 

 

在实验的测试环境中Post data一栏就是攻击者恶意修改的数据,Load URL一栏就是进行SQL注入测试的网址,点击Execute提交测试的数据:

万能密码的测试结果中看到成功登陆了,还可以使用test1用户进行发留言和删除,退出等操作。但是现在我们对于万能密码漏洞产生的原因仍然无从得知,为什么攻击者恶意伪造数据就能成功登陆?其实问题的根源就是代码逻辑不严谨,缺乏安全意识导致的。

 

为了进一步分析漏洞产生的原因,我们需要通过分析代码来了解前后端的数据是如何交互的,以及后端和数据库之前如何交互的。我们知道的是,点击Execute提交数据时,测试数据就随着测试的网址从前端传入到后端。

 

 

分析登录功能对应的源代码:


  
  1. <?php
  2. /**
  3. * Created by TEST -- 用户登录
  4. */
  5. require "./lib/init.php";
  6. header( "Content-type:text/html;charset=utf-8");
  7. if( empty($_POST))
  8. {
  9. //header('Location: register.php');
  10. echo "登录信息为空";
  11. header( "Refresh:3;url=login.php");
  12. } else{
  13. //是否设置用户名和密码
  14. if( isset($_POST[ "user_name"]) && isset($_POST[ "user_pass"])){
  15. //去除空格字符
  16. $usename = trim($_POST[ "user_name"]);
  17. $password = trim($_POST[ "user_pass"]);
  18. //对密码进行加密,然后拼接成SQL语句
  19. $password = md5($password);
  20. $sql = "select * from users where user_name='$usename' and user_pass='$password'";
  21. //var_dump($sql);
  22. //exit();
  23. //查询数据库
  24. $selectSQL = new MySql();
  25. $user_data = $selectSQL->getRow($sql);
  26. if($user_data!= "")
  27. {
  28. session_start();
  29. $_SESSION[ "user"] = $user_data[ "user_name"];
  30. header( "Location: user.php");
  31. } else{
  32. echo "用户名或者密码错误!";
  33. header( "Refresh:3;url=login.php");
  34. }
  35. } else{
  36. echo "登录信息不完整";
  37. header( "Refresh:3;url=login.php");
  38. }
  39. }

在以上的代码中,对于前端传入的用户名和密码等数据,后端只是简单的去除空格字符并没有进行严格的校验和过滤,直接将用户名和密码拼接成SQL语句执行。

 

 

使用var_dump函数把拼接后的SQL语句打印出来:

 

最终拼接出来的SQL语句是这样的:select * from users where user_name='123' or 1#' and user_pass='81dc9bdb52d04dc20036dbd8313ed055'  ,重要的是这条SQL语句在MYSQL数据库中是一定能执行成功的

通过分析可知,前端传入的用户名和密码中带有“#”特殊字符,在MYSQL数据库中“#”字符后面的内容会被当做注释,但是后端代码并没有对“#”字符进行过滤而是直接拼接成SQL语句,也就是说,在这条SQL语句中“#”字符往后的全都当做注释了,这就导致最终拼接的SQL语句实际上是这样的:select * from users where user_name='123' or 1 。

 

MYSQL查询到了所有的数据,却只返回了第一条数据,在前端页面为啥是用的test1用户登录的呢?接着分析getRow函数的实现:


  
  1. //查询单行
  2. public function getRow($sql){
  3. $res = $this->link->query($sql);
  4. //只截取第一条记录
  5. $row = $res->fetch_assoc();
  6. return $row;
  7. }

数据库查询到的所有数据都放在res对象中,但fetch_assoc函数只从res对象中截取了第一条数据并返回结果,而第一条数据就是test1用户,前端页面登录时用的恰好就是test1用户。

 

 

5. 如何防止SQL注入

以上面的用户登录为例,后端在接收前端传入的数据时,例如一些特殊字符应该使用PHP的addslashes函数和mysqli_real_escape_string函数进行过滤。

 

对用户登录功能的代码改进:

 


  
  1. //去除空格字符
  2. $usename = addslashes(trim($_POST[ "user_name"]));
  3. $password = addslashes(trim($_POST[ "user_pass"]));

这两个函数的作用就是对 SQL语句中的特殊字符进行转义,当后端接收到前端传入的数据时,addslashes函数会对特殊字符进行转义,然后再进行测试:

 

 

由于addslashes函数会对数据中的特殊字符进行过滤转义,此时登录就会报错,可以看到addslashes函数对用户名中的“ ’”特殊字符使用斜杠字符进行转义了:

 

这条SQL语句在MYSQL数据库中无法查询到数据的:

 

通过一个简单的小案例我们基本了解了SQL注入的原理,产生SQL注入的原因以及如何防御的技巧。前端传入后端的参数可以由用户控制,且参数拼接成SQL语句会被带入数据库中执行。在开发过程中只要满足上述所说就有可能产生SQL注入漏洞,因此在开发过程中应秉承“外部参数皆不可信”的原则进行开发。

 


转载:https://blog.csdn.net/qq_35733751/article/details/105802390
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场