XDSec—12SQL注入基础总结
目录
一、php数据库编程
本小节简要说明php数据库编程的一些基础概念,旨在更好的理解sql注入漏洞的产生原因和攻击方式;以mysql数据库为例;php自身并无数据库编程的能力,需要安装mysqli或PDO模块,以此来完成数据库编程的功能;这里以mysqli为例;
在php5.0版本之后,php使用面向对象的数据库编程方式;我们常用的sqli-libs靶场用的是过程式的mysqli函数来操作数据库的,两者并无太大区别。
1.1、php数据库编程的三大步骤
1.1.1、连接数据库
创建mysqli这个类的对象。mysqli的构造方法大致如下:
mysqli::__construct(<host>,<username>,<passwd>,<database>,[port]);
// host为mysql的地址
// username和passwd分别为数据库的管理员账户和密码
// database为选择的数据库名称
// port一般默认3306,若自行修改了端口,则要添加该值
// 其中database也可用其他成员方法更换,具体自行了解
用例如下:
$con =new mysqli("localhost","root","root","phpDB");
// 连接localhsot下的phpDB,并将对数据库的引用返回为$con该变量
// $con就是连接数据库资源的对象的引用变量
1.1.2、执行sql命令
使用mysqli类中定义的query()方法,该方法若成功执行sql语句则返回true,反之false
$sql ="insert into users(user_id,user_passwd) values('admin','123456')";
// 定义sql语句
if($con->query($sql)) // 调用query()方法,并传入要执行的sql语句
{
echo "改变的记录的数量:".$con->affected_rows."<br>";
}
上述操作只适用于无返回值的增删改操作
1.1.3、对于查询操作的处理
若$con->query($sql)
执行了一个select语句,则会以mysqli_result
类的对象形式返回结果集;
结果集也是一张表;对于结果集的处理有以下四种方式,前三种方法可以看作从结果集中取出一条记录,以数组的形式返回,数组的下标分别对应每个字段,数组的值就是每个字段的值:
-
fetch_row()
:将普通数字索引数组形式返回结果集的记录,数字下标从0开始,分别对应表中的字段,记录的值就是数组各单元的值 -
fetch_assoc()
:以关联数组的形式返回结果集中的记录,关联数组的下标就是各字段名称,值与下标一一对应 -
fetch_array()
:以上面两种形式结合的数组返回,php支持一个数组的下标的由多种形式组成,只要不重复 -
fetch_object()
:以对象形式返回,对象的成员属性即为结果集的各个字段,调用成员属性即可输出该记录各字段对应的值示例如下:
// 仍使用上述$con的连接 $sql ="select * from users"; $result =$con->query($sql); // 执行了查询语句,返回一个结果集对象 // 该对象可理解为指向结果集表的一个指针 while($row =$result->fetch_assoc()) // 取出结果集中的记录,以关联数组形式返回 // 该方法在成功返回一条记录后,会自动移动到下一条记录,全部结束则返回false { echo $row["user_id"]; echo $row["user_passwd"]; // 循环输出结果集中每条记录的信息 }
1.1.4、断开连接
对于结果集对象和数据库连接对象都要进行资源回收
$result->close(); // 回收结果集资源
$con->close(); // 断开数据库连接
总结:数据库编程的三大步骤
- 建立连接
- 执行sql命令并处理结果集
- 断开连接
1.2、数据库编程的一般应用
本小节主要简单总结前端与后端的sql语句交互,可以简单理解sql注入漏洞的来源,以及如何做防范
1.2.1、sqli-libs源码分析
Less-1的部分源码,及其简要分析
if(isset($_GET['id'])){ // 该id变量即为通过URL传递的一个变量
$id=$_GET['id'];
// 在前端设计中,该id可以是前端的一个表单域中的控件的name值
// 用户在控件中填写值后,点击submit控件提交,整个表单域的内容就会以键值对的形式传递到action指定的后端文件中
// 如果表单采用get提交,则键值对会通过url传递;post则在请求报文的请求体中出现
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
// 该条sql语句通过用户传递的$id与数据库交互,查询到指定内容
// 分析这里便是sql注入的核心,可以发现如果不对$id进行严格检查,就会使发生sql注入的语句被数据库执行
echo '执行的sql语句为:'.$sql;
echo '<br>';
echo '<br>';
$result=mysql_query($sql);
// 采用过程式的函数传递sql语句,同对象式的成员方法query()
$row = mysql_fetch_array($result);
// 对结果集进行处理,将记录以数字索引和关联两种形式的数组取出
// 之后就可以通过访问数组的方式对记录进行输出
1.2.1、预处理与占位符
php还提供了另一种形式的sql语句交互方式,即通过mysqli_stmt
这个类进行预处理,在sql语句中使用占位符?
,并将占位符与自定义变量绑定,从而实现sql语句的交互
案例:
$sql ="select stu_num,stu_name from stus where stu_num=?";
// 使用占位符,占位符可以与前端传来的数值绑定,从而实现交互
$stmt =$con->prepare($sql); // 获取预处理的sql语句对象
$stmt->bind_param("s",$num); // 为占位符绑定变量并且指定类型为s,因为数据库是强类型的
// i(所有整型)、d(所有浮点值)、s(其他,包括字符串)、b(所有二进制类型)
// 若bind->param("sdsi",$v1,$v2,$v3,$4),即分别向四个变量指定类型
$num ="20220001"; // 给变量赋值
// 这个变量的值可以是前端控件传来的,实现前后端交互,并可对值进行严格性判断,防止sql注入
$stmt->execute(); // 执行预处理的SQL语句
// 此时就实现了交互式的sql语句
$stmt->store_result();// 取出结果集表
$stmt->bind_result($stu_num,$stu_name); // 这两个变量是为了匹配返回结果集中的字段,不要和绑定占位符的变量混淆
$stmt->fetch();// 取出结果集表中的记录
echo $stu_num."----".$stu_name;
二、针对mysql的注入技巧总结
mysql开源免费,因此是许多网站选择的数据库,并且它内置了好多功能函数,以及几个用来便于DBA管理的元数据库,这些函数和数据库都会成为我们进行sql注入时的兵器
2.1、mysql常用函数
2.1.1、系统信息函数
-
version():输出数据库的版本
使用
select <函数>;
来输出,下同 -
database():输出当前所在数据库
-
user():输出数据库用户名
-
currnet_user():输出当前用户名
-
system_user():输出系统用户名
-
@@datadir:数据库路径
-
@@version_compile_os:操作系统的版本
2.1.2、字符串操作函数
-
length():返回字符串的长度
-
substring()、substr()、mid():字符串截取函数
分别都有三个参数
-
截取的字符串
-
截取起始位置,从一开始计数
-
截取长度
-
-
left():从左侧开始取指定字符个数的字符串
-
concat():没有分隔符的字符串拼接函数
-
concat_ws():含有分隔符的字符串拼接函数
-
group_concat():连接一个组的字符串,通常用于将同一个字段的数据拼接为同一行输出
将id字段的数据拼接为一行输出
2.1.3、编码函数
-
ord():以ascii码返回,同ascii()
-
hex():将字符串转为16进制
-
unhex():十六进制解码
-
md5():返回md5值
-
floor(x):返回不大于x 的最大整数
-
round():返回参数x 接近的整数
-
rand():返回0-1 之间的随机浮点数
2.1.4、时间函数
-
sleep(s):睡眠时间为指定的秒数,睡眠期间数据库无法进行任何操作
2.1.4、其他函数
- load_file():读取文件并返回文件内容为一个字符串
- if():判断函数
- find_in_set():返回字符串在字符串列表中的位置
2.2、information_schema数据库
2.2.1、基本了解
-
该数据库同performance_schema(用于性能分析),是mysql自带的数据库
-
作用:用于存储元数据,关于所有数据库的一切对象几乎都包含在内,如数据库名、数据表名、列名及其类型、权限等
该数据库存储的数据都是所有数据库对象的视图,与原数据无关联
2.2.2、常用表及其字段组成
-
schemata表:存储所有数据库的名称,
show tables
就是从该表获取数据的重要字段:schema_name,即所有数据库名称
-
tables表:存储所有的表名
重要字段:
- table_name,即所有的表名称
- table_schema,即表对应的数据库名称
-
columns:存储所有的字段名称
重要字段:
- table_schema,即字段名称对应的数据库名称
- table_name,即字段所在的表的名称
- columns_name,所有字段的名称
掌握该数据库常用的表及其重要字段,可以在sql注入猜表名、字段名时提供帮助
三、搭建sqli-libs靶场
-
使用docker容器
# docker pull acgpiano/sqli-labs # docker run -dt --name sqli -p 80:80 -p 13306:3306 --rm acgpiano/sqli-labs # docker exec -it sqli /bin/bash
第二句命令是为了把docker的端口映射到主机端口,详细教程请看https://blog.csdn.net/Alashan12/article/details/124027184
-
使用phpstudy(新手推荐)
-
从GitHub下载靶场文件:https://github.com/Audi-1/sqli-labs
-
解压后放入phpstudy的WWW目录下
-
在小皮面板创建网站,填写域名等信息,并且要创建数据库,php版本一定要选择php5(因为该靶场使用的mysqli是过程式的函数,php5之后的都成了面向对象的mysqli)
-
可以修改本地hosts,然后就可以在浏览器使用域名访问靶场了
-
打开靶场文件
db-creds.inc
,填写数据库密码,这步一定要正确,否则会建立数据库失败 -
浏览器进入靶场,如图所示,点击Setup/reset Database for libs初始化数据库
-
初始化成功后如下图所示,然后就可以开始练习sql注入啦
-