文章目录

  • 1.前言
  • 2.实验环境搭建
  • 3.原理介绍
    • 3.1 HTTP协议
      • 3.1.1 HTTP报文
      • 3.1.2 GET方法
      • 3.1.3 POST方法
      • 3.1.4 从页面到报文
    • 3.2 SQL注入
      • 3.2.1 GET注入
        • (1)验证GET传参
        • (2)SQL注入
      • 3.2.1 POST注入
        • (1)验证POST传参
        • (2)SQL注入

1.前言

SQL注入是Web安全层面最高危的漏洞之一,长期霸榜OWASP Top10首位,但是究竟什么事SQL注入?SQL注入又是怎么产生的?接下来本篇文章将详细介绍SQL注入产生的原理。本篇文章并没有描述具体的注入方法,而是侧重于对原理的描述,并分别分析了GET和POST注入。

2.实验环境搭建

在弄清SQL注入的基本原理之前,我们首先要来搭建实验的环境,我们需要一台L(Linux)NMP服务器或者W(Windows)NMP服务器,具体服务器的搭建可以参考我之前的文章《一篇文章告诉你如何搭建LNMP》,或者直接使用phpStudy完成搭建。
完成服务器的搭建后,我们要准备好测试用的数据库,执行下面的SQL命令完成数据库的建立

create database SQLInjection;
use SQLInjection
create table users(id int,username varchar(255),password varchar(255));

创建完成后,可以插入几条用于测试的数据

insert into users value(1,'Tom','123456');
insert into users value(2,'Alice','654321');
insert into users value(3,'Jerry','qwerty');

3.原理介绍

3.1 HTTP协议

HTTP协议是Web应用层协议,全称是超文本传输协议。这个协议是Web应用的核心,HTTP由两个程序实现,一个是客户端程序,通常是我们使用的浏览器,一个是服务器程序,运行在我们所访问俄的服务器上。这两个程序通过HTTP协议进行交流,HTTP定义了这些报文的语法、语义和时序。这里我们并不需要对HTTP协议做过深的了解。但是我们依旧需要明确以下几个知识点:

  1.客户端程序与服务器程序之间是通过报文进行交流的,通常是客户端请求数据,服务器响应客户端请求的数据;2.报文是包含了不同的字段,不同的字段代表的不同的信息3.报文中携带了客户端程序与服务器程序之间通信的数据4.数据在报文中的位置不同,服务器接收该数据的方式也不同

3.1.1 HTTP报文

客户端与服务器端的报文,根据发送方的不同被分为请求报文和响应报文,从客户端发往服务器端的被称为请求报文,从服务器端发往客户端的被称为响应报文。请求报文和响应报文都有不同的固定格式,请求报文的一般报文格式如下

响应报文的一般格式如下

因为本文主要主要讲述的是SQL注入,SQL注入我们是无法操作服务器的,只能通过影响从客户端发送的请求报文进而从服务器获取数据,所以我们只需要了解请求报文,在上面的请求报文结构图中,我们可以看到请求报文主要包含请求行、首部行、空行、实体体4个模块。各个模块的功能说明如下

模块名 说明
请求行 方法 说明传递数据的方法
URL 指明想要访问的文件
版本 协议的版本HTTP /1.1
首部行 首部行中包含众多的字段,不同的报文可能会携带不同的字段,在请求报文中主要用来说明客户端的相关信息
空行
实体体 当使用POST方式传递数据的时候,该模块会填充POST想要传递的数据

在前面的内容中不断的提到数据的传递,数据传递的方法会在请求行的方法中说明,主要有4种数据传递的方法,这里只介绍两种与SQL注入有关的

3.1.2 GET方法

当请求报文的方法字段被设置为GET的时候,表明该请求报文使用GET方式进行传参。通常GET方法是用来获取服务器上的指定文件,比如

GET /index.php HTTP/1.1

上面的请求行用来获取网站根目录下的index.php文件,当站点接收到该请求后会将用户请求的文件内容进行解析,然后通过响应报文响应用户的请求。但是也会遇到某些文件需要接收参数的情况,客户端程序会使用下面的方式对文件完成传参

GET /index.php?id=1 HTTP/1.1

这样服务器上的文件便能够接受id=1的传参。然后根据用户的传参作出响应的处理。

3.1.3 POST方法

当请求报文的方法字段被设置为POST的时候,表明该请求报文使用POST方式进行传参。通常POST方式用来提交用户输入的表单信息,这些协议存放在请求报文的实体体中。比如

POST /user/login.html HTTP/1.1
//......
user=admin&pass=admin

上面是一个用户登录报文的示例,通过POST方式提交用户输入的用户名和密码。

3.1.4 从页面到报文

上面所说到的报文或者传参方式都是客户端程序生成的,作为用户只需要在页面上点击鼠标或者输入内容即可。那么客户端程序是怎么确定要使用什么样的方式传参呢?简单来说我们在表格中输入的数据会通过POST进行传参,而我们的点击页面的操作一般会通过GET传参。
POST传参的方式比较好理解,但是我们还要重点说明一下GET方式,比如当我们浏览某个站点的时候,点击了站点中的一篇文件,页面就给我们显示了我们点击的文章。现在我从数据包的角度分析站点为什么可以准确的显示我们所点击的文章。首先我们点击文章,服务端会发送类似于下面的数据包

GET /index.php?p=1234 HTTP/1.1
Host: www.test.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html, */*; q=0.01
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
X-Requested-With: XMLHttpRequest
Connection: close
Referer: http://www.test.com

可以看到由于是GET方法,所以报文的实体体为空。Host字段表示站点的域名。通过域名可以找到该站点的服务器,然后将报文传递到该站点,站点接收报文后发现用户请求的是/index.php文件,并且有传参p=1234,站点便会在根目录下寻找index.php文件,执行index.php文件并完成传参,执行的结果就是我们请求的文章,然后将文章发送给我们。我们会发现当页面显示文章后,浏览器的的URL栏变成了

可以看到URL栏会显示我们通过GET方式传递的参数p=1234。其实我们通过GET方式对文件传递的参数都会显示在URL栏中,我们可以直接修改URL栏中的内容为

http://www.test.com/?p=1234

或者

http://www.test.com/index.php?p=1234

都会发送同样的数据包,显示同样的内容。也就是说我们可以通过修改URL栏中的内容控制传参的内容。
补充:因为站点会默认请求index.php或者index.html文件所以在请求index.php文件的时候可以不写index.php文件,站点就会直接请求index.php文件。

3.2 SQL注入

说到这里我们才正式进入SQL注入的原理分析,但是看完上面的内容我们已经完成一大半了,接下来就是将上面的知识应用到SQL注入中。这里仅仅介绍两种典型的注入GET和POST。SQL注入的方式有很多但是基本原理都差不多的。
站点存在SQL注入必须满足下面的三个条件

1.用户能够控制输入
2.待执行的代码拼接了用户输入的数据
3.执行了待执行的代码

3.2.1 GET注入

(1)验证GET传参

首先我们来模拟一下上传的传参方式,首先在我们的LNMP或者WNMP服务器(下称“服务器”)的网站的某个你喜欢的目录下放置下面的脚本
test.php

<?php
echo 'hello,world!';
$id=$_GET['id']; #接收GET传参
echo $id;
?>

这里将该脚本放置在SQLInjection目录下,随后我们在URL栏输入路径访问该文件,页面显示效果如下

页面成功的显示了hello,world!,接着我们需改URL栏为

http://192.168.25.147/SQLInjection/test.php?id=1234

并抓取浏览器发送的数据包,数据包内容如下

GET /SQLInjection/test.php?id=1234 HTTP/1.1
Host: 192.168.25.147
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Cookie: PHPSESSID=0871f074b43a28eb3e15dcf7b0635b6d
Upgrade-Insecure-Requests: 1
Cache-Control: max-age=0

可以看到该数据包使用GET方式进行传参,并在URL字段指定了文件的路径和我们对文件的传参。同时返回的页面显示了我们在URL栏中传递的参数

上面的操作也就验证了通过URL栏的方式传递参数是GET方式的传参。并且测试了我们的文件可以接受GET传参的内容。

(2)SQL注入

将上面的代码修改为

echo 'hello,world!'.'<hr/>';
#1.获取get传参
$id=$_GET['id'];
echo '你传入的参数'.$id.'<hr/>';
#2.链接数据库
$conn=mysqli_connect('localhost','root','root','sqlinjection');
if(!$conn) die("<script>alert('Sorry,cannot connect to database')</script>");
#3.构造SQL命令
$sql="select * from users where id=$id";
echo '执行的SQL语句'.$sql.'<hr/>';
#4.查询数据库
$result=mysqli_query($conn,$sql);
#5.显示查询结果
echo '查询结果'.'<br/>';
while($row=mysqli_fetch_array($result))
{echo 'id:'.$row['id'].';'.'username:'.$row['username'].';'.'password:'.$row['password'].'<br/>';
}
#6.断开链接
mysqli_close($conn);

代码修改完成后,我们接着访问该代码,然后传入参数id=1,页面显示如下

可以看到站点成功的接收了我们的传参,并根据我们的传参访问数据库,并显示了数据库的访问结果。这是正常情况下的传参,可以理解为我们登录用户后,页面通过GET传参的方式从数据库中取出我们的用户信息显示在页面上。因为我们可以控制传参的内容,那我们尝试将URL栏修改为

http://192.168.25.147/sqlinjection/test.php?id=1 union select * from users where id=2

站点显示的内容变成了

也就是说我们修改URL栏中的内容将id的值变成了

id=1 union select * from users where id=2

站点在接收到该值后,将该变量的值与SQL命令进行拼接,形成了如下的SQL命令

select * from users where id=1 union select * from users where id=2

随后执行了该SQL命令,显示了预期之外的数据。
在正常操作下id接收的是一个数字,然后将该数字拼接到SQL命令中执行。但是我们通过人为修了id传参的值,也就是说我们可以控制id的值,并将其修改为一个恶意的值;接着系统会将我们输入的内容拼接道SQL命令中,即待执行的代码拼接了用户输入的内容;最后拼接了用户输入内容的代码执行了,获取了数据库中的数据。

3.2.1 POST注入

理解了前面的GET注入的原理,POST注入的原理和GET完全一致,不同点在于传参方式的不同。

(1)验证POST传参

准备了如下的程序将其放置在sqlinjection目录下
login.html

<!DOCTYPE HTML>
<html><head><meta charset="UTF-8"><title>login</title></head><body><form action="login.php" method="POST" target="_self">username:<input type="text" name="username"/><br />password:<input type="password" name="password"/><br /><input type="submit" value="login"><hr /></form></body>
</html>

login.php

<?php
#1.从前端获取数据
$username=$_POST["username"];
$password=$_POST["password"];
echo '你输入了如下的内容'.'<br/>';
echo 'username:'.$username.'<br/>';
echo 'password:'.$password.'<hr/>';
?>

随后我们访问login.html,页面显示如下

输入如下的内容后

字段名 内容
username Tom
password 123456

点击login并抓取数据包,得到的数据包内容如下

POST /sqlinjection/login.php HTTP/1.1
Host: 192.168.25.147
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 28
Origin: http://192.168.25.147
Connection: close
Referer: http://192.168.25.147/sqlinjection/login.html
Cookie: PHPSESSID=0871f074b43a28eb3e15dcf7b0635b6d
Upgrade-Insecure-Requests: 1username=Tom&password=123456

可以看到数据包使用的是POST的方式,并且在实体体中携带了我们输入的用户名和密码信息。随后页面会显示下面的内容

可以看到我们在表单是输入的内容是使用POST方式携带在实体体中进行传参的。同时login.php能够成功的接收我们传入的数据

(2)SQL注入

将login.php脚本修改为

<?php
#1.接收用户传参
$username=$_POST["username"];
$password=$_POST["password"];
echo '你输入了如下的内容'.'<br/>';
echo 'username:'.$username.'<br/>';
echo 'password:'.$password.'<hr/>';
#2.链接数据库
$conn=mysqli_connect('localhost','root','123456','sqlinjection');
if(!$conn) die("<script>alert('Sorry,cannot connect to database,please login later!')</script>");
#3.构造查询语句
$sql="select * from users where username='$username' and password='$password'";
echo '执行的SQL命令为:'.$sql.'<hr/>';
#4.进行查询并获得结果
$result=mysqli_query($conn,$sql);
$row=mysqli_fetch_array($result);
if(isset($row))
{echo '---登录成功---'.'<hr/>;';
}else{echo '---登录失败---'.'<hr/>';
}
?>

当我们输入正确的用户名和密码的时候页面会显示

如果输入的用户名或者密码是错误的时候,页面会显示

也就是说站点能够正确的验证用户名和密码的正确情况,但是我们尝试输入下面的内容

字段名 字段值
username Tom
password ’ or 1=1 or ’

我们发现居然也登录成功了

仔细观察我们输入的内容拼成的SQL语句

select * from users where username='Tom' and password='' or 1=1 or ''

在执行select的时候判断条件有4个,分别是

条件1.username='Tom'
条件2.password=''
条件3.1=1
条件4.''

其中条件1与条件2使用and链接,其他的条件使用or链接,当程序从左往右判断条件成立的时候,执行到条件1的时候能找到使条件成立的记录,虽然执行到条件2的时候发现不成立,但是因为后面使用了or连接了一个恒成立的式子1=1,因此重要1=1存在其他的条件就算不成立也可以查找到对应的记录。所以在这里即使我们不是输入密码也可能成功的登录。这也是常用的万能密码。
以上便是POST注入的基本原理,可以看到和GET注入类似。当我们输入用户名的时候可以输入任意的内容,也就是说用户可以控制输入。当程序带着我们输入的内容查找数据库的时候,也就是拼接我们输入的内容并执行。

究竟什么是SQL注入?相关推荐

  1. sql参数化还是被注入了_面试官问你 SQL 注入攻击了吗?

    目录 为什么要聊 SQL 注入攻击? 什么是 SQL 注入攻击? 如何进行 SQL 注入攻击? 如何防范? 常见面试题 瞎比比 为什么要聊 SQL 注入攻击? 我这人有个想法,就是不管自己跳不跳槽,每 ...

  2. [网络安全提高篇] 一〇六.SQL注入之手工注入和SQLMAP入门案例详解

    当您阅读到该篇文章时,作者已经将"网络安全自学篇"设置成了收费专栏,首先说声抱歉.感谢这一年来大家的阅读和陪伴,这100篇安全文章记录了自己从菜鸡到菜鸟的成长史,该部分知识也花了很 ...

  3. [网络安全提高篇] 一〇五.SQL注入之揭秘Oracle数据库注入漏洞和致命问题(联合Cream老师)

    当您阅读到该篇文章时,作者已经将"网络安全自学篇"设置成了收费专栏,首先说声抱歉.感谢这一年来大家的阅读和陪伴,这100篇安全文章记录了自己从菜鸡到菜鸟的成长史,该部分知识也花了很 ...

  4. [网络安全提高篇] 一〇四.网络渗透靶场Oracle+phpStudy本地搭建万字详解(SQL注入、XSS攻击、文件上传漏洞)

    当您阅读到该篇文章时,作者已经将"网络安全自学篇"设置成了收费专栏,首先说声抱歉.感谢这一年来大家的阅读和陪伴,这100篇安全文章记录了自己从菜鸡到菜鸟的成长史,该部分知识也花了很 ...

  5. metinfo mysql_Metinfo 5.3.17 前台SQL注入漏洞

    Metinfo 8月1日升级了版本,修复了一个影响小于等于5.3.17版本(几乎可以追溯到所有5.x版本)的SQL注入漏洞.这个SQL注入漏洞不受软WAF影响,可以直接获取数据,影响较广. ### 0 ...

  6. java安全(二):JDBC|sql注入|预编译

    给个关注?宝儿! 给个关注?宝儿! 给个关注?宝儿! 关注公众号:b1gpig信息安全,文章推送不错过 1 JDBC基础 JDBC(Java Database Connectivity)是Java提供 ...

  7. 白帽子发现美军网站SQL注入漏洞,可获取敏感数据

    去年有报道称,美军收购软件漏洞为网战准备.而美军自己的网站和服务器究竟又有多安全?一名独立安全研究者已经发现了美军网站的几个较为严重的安全漏洞. 安全专家称,这些漏洞说明了美国防部网络安全基础的脆弱性 ...

  8. 为什么preparedstatement能防止sql注入_使用Python防止SQL注入攻击的实现示例

    文章背景 每隔几年,开放式Web应用程序安全项目就会对最关键的Web应用程序安全风险进行排名.自第一次报告以来,注入风险高居其位!在所有注入类型中,SQL注入是最常见的攻击手段之一,而且是最危险的.由 ...

  9. C#SQL注入检测——特别是对于旧版.NET代码

    目录 使用Decorator模式提供添加SQL注入检测的位置 SQL注入检测代码 究竟如何检测到SQL注入? SQLExtensions类中包含的格式化方法 自定义.NET异常类 用于检测SQL注入的 ...

最新文章

  1. 【Java】使用单向链表实现一个队列(Queue)
  2. 招聘|字节跳动招计算机视觉方向算法实习生
  3. 线性代数带参数的线性方程组的求法示例详解
  4. leetcode 292. Nim Game | 292. Nim 游戏(DP->数学推理)
  5. 预见2019:《2019年中国视频监控产业全景图谱》(附产业布局、政策环境、市场规模、发展趋势)
  6. linux svn cleanup 用法,SVN命令之清理命令(clean up)的功能及使用技巧
  7. eclipse搜索框太小
  8. angular开发中对请求数据层的封装
  9. 中国农业工作服行业市场供需与战略研究报告
  10. ios打开html页面关闭当前页面跳转,【已解决】怎么从iOS原生界面跳转回到html页面呢...
  11. html在线拖拽环绕,用HTML5原生实现拖放或排序
  12. 深度剖析Spring Cloud底层原理
  13. 你真的会用ABAP, Java和JavaScript里的constructor么? 1
  14. 目前流行的前端UI框架
  15. 计算机网络ppt背景,教大家使用ppt设计出高逼格的背景图
  16. 市场调研报告-工业固体废物管理市场现状及未来发展趋势
  17. 工作三年的Java程序员该如何规划后续的职业发展?
  18. 如何像伟大的企业家一样写作
  19. 每日文献:2018-01-29
  20. dnf剑魂buff等级上限_DNF:深渊之鳞护肩宝珠选择不再纠结,全职业BUFF等级一览

热门文章

  1. 除了社交,支付宝圈子还在说一个更大的故事
  2. 对bochs设置文档的初步翻译
  3. 程序猿最终之路-架构师:Spring boot实战战役解析
  4. 疫情时期的5G是“伪需求”?
  5. qq服务器运行失败,QQ连接服务器超时,请重试,错误码0x00000001解决方法分享
  6. mac利用vnc远程连接服务器ubuntu16出现灰屏和鼠标变黑色x的解决方法
  7. 优美语句人生励志语录
  8. Science:中国农业科学院作物科学研究所周文彬团队在水稻中发现单一基因可使水稻显著增产...
  9. 本周资讯top3 【越狱苹果】独家探寻阿里安全潘多拉实验室,完美越狱苹果iOS11.2.1 知道如何从攻击的视角去发现漏洞,才能建立更安全的体系,促进了整个生态的良性发展。以阿里安全潘多拉实验室为例
  10. 函数传递的参数是原参数的副本