PHP代码审计笔记--SQL注入

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS MySQL,高可用系列 2核4GB
简介:     0X01 普通注入 SQL参数拼接,未做任何过滤 测试语句:id=1 UNION SELECT user(),2,3,4 from users 0x02 宽字节注入 A、MYSQL中的宽字符注入 示例代码: 测试语句:%df%27 mysql的特性,因为gbk是多字节编码,两个字节代表一个汉字,所以%df和后面的\也就是%5c变成了一个汉字“運”,而’逃逸了出来。

   

0X01 普通注入

SQL参数拼接,未做任何过滤

<?php
$con = mysql_connect("localhost","root","root");
if (!$con){die('Could not connect: ' . mysql_error());}
mysql_select_db("test", $con);
$id = stripcslashes($_REQUEST[ 'id' ]);
$query  = "SELECT * FROM users WHERE id = $id ";
$result = mysql_query($query)or die('<pre>'.mysql_error().'</pre>');
while($row = mysql_fetch_array($result))
  {
  echo $row['0'] . " " . $row['1'];
  echo "<br />";
  }
echo "<br/>";
echo $query;
mysql_close($con);
?>

测试语句:id=1 UNION SELECT user(),2,3,4 from users

0x02 宽字节注入

A、MYSQL中的宽字符注入

示例代码:

<?php
$con = mysql_connect("localhost","root","root");
mysql_query("SET NAMES 'gbk'");
mysql_select_db("test", $con);
$id = isset($_GET['id']) ? addslashes($_GET['id']) : 1;
$query  = "SELECT * FROM users WHERE id ='{$id}' ";
$result = mysql_query($query)or die('<pre>'.mysql_error().'</pre>');
while($row = mysql_fetch_array($result))
  {
  echo $row['0'] . " " . $row['1'];
  echo "<br />";
  }
echo "<br/>";
echo $query;
mysql_close($con);
?>

测试语句:%df%27

mysql的特性,因为gbk是多字节编码,两个字节代表一个汉字,所以%df和后面的\也就是%5c变成了一个汉字“運”,而’逃逸了出来。

根据gbk编码,第一个字节ascii码大于128,基本上就可以了。比如我们不用%df,用%a1也可以.

gb2312编码的取值范围。它的高位范围是0xA1~0xF7,低位范围是0xA1~0xFE,而\是0x5c,是不在低位范围中的。所以,0x5c根本不是gb2312中的编码,所以不会造成宽字节注入。扩展到世界上所有多字节编码,只要低位的范围中含有0x5c的编码,就可以进行宽字符注入。

宽字符注入的修复:

方案一:指定php连接mysql的字符集

mysql_set_charset('gbk',$conn);

$id =mysql_real_escape_string($_GET['id']);

方案二:将character_set_client设置为binary(二进制)

mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $conn); 

将character_set_client设置成binary,就不存在宽字节或多字节的问题了,所有数据以二进制的形式传递,就能有效避免宽字符注入。

 

B、PHP 编码转换

示例代码:

<?php
$con = mysql_connect("localhost","root","root");
mysql_query("SET NAMES 'gbk'");
mysql_select_db("test", $con);
mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $con); 
$id = isset($_GET['id']) ? addslashes($_GET['id']) : 1;
$id=iconv('utf-8','gbk',$id);
$query  = "SELECT * FROM users WHERE id ='{$id}' ";
$result = mysql_query($query)or die('<pre>'.mysql_error().'</pre>');

while($row = mysql_fetch_array($result))
  {
  echo $row['0'] . " " . $row['1'];
  echo "<br />";
  }
echo "<br/>";
echo $query;

mysql_close($con);

?>

测试语句: 錦'

錦这个字:它的utf-8编码是%e9%8c%a6,它的gbk编码是%e5%5c

錦被iconv从utf-8转换成gbk后,变成了%e5%5c,而后面的’被addslashes变成了%5c%27,这样组合起来就是%e5%5c%5c%27,两个%5c就是\\,正好把反斜杠转义了,导致’逃逸出单引号,产生注入。

$id=iconv('gbk','utf-8',$id);  //使用%df%27来测试

一个gbk汉字2字节,utf-8汉字3字节,如果我们把gbk转换成utf-8,则php会每两个字节一转换。所以,如果\’前面的字符是奇数的话,势必会吞掉\,’逃出限制。

其他函数:

mb_convert_encoding($id,'utf-8','gbk')  //GBK To UTF-8与 iconv('gbk','utf-8',$id)一样

参考文章:

  PHP字符编码绕过漏洞总结   http://www.cnblogs.com/Safe3/archive/2008/08/22/1274095.html

  浅析白盒审计中的字符编码及SQL注入    http://www.freebuf.com/articles/web/31537.html

 

0x03 编码解码

找一些编码解码的函数来绕过防护,,如urldecode() 、rawurldecode()、base64_decode()

<?php
$con = mysql_connect("localhost","root","root");
mysql_select_db("test", $con);
$id = addslashes($_REQUEST['id']);
$id = urldecode($id);//$id = base64_decode($id);
$query  = "SELECT * FROM users WHERE id = '{$id}'";
$result = mysql_query($query)or die('<pre>'.mysql_error().'</pre>');

while($row = mysql_fetch_array($result))
  {
  echo $row['0'] . " " . $row['1'];
  echo "<br />";
  }
echo "<br/>";
echo $query;
mysql_close($con);
?>

测试语句:

  1'union select 1,2,3,4%23    单引号Urlencode           1%2527union select 1,2,3,4%23

  1'union select 1,2,3,4# Base64 Encode MSd1bmlvbiBzZWxlY3QgMSwyLDMsNCM=

0x04 二次注入

  入库后转义符就会消失,变成hack',查询出库的就是hack',如果拼接到SQL语句,成功引入了单引号闭合前面字符,导致注入。

测试:

   CREATE TABLE  test (user VARCHAR(20) NOT NULL);

  INSERT INTO test values('hack\'');

  

示例代码:

//测试数据
create table test(
id INT NOT NULL,
user VARCHAR(100) NOT NULL,
pass VARCHAR(100) NOT NULL
)
INSERT INTO test values(1,'hack','hack');

//测试代码
<?php
$con = mysql_connect("localhost","root","root");
mysql_select_db("test", $con);
mysql_query("SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary", $con); 
//update入库
if (isset($_GET['key'])){
    $key=addslashes($_REQUEST['key']);
    $query ="update test set user='{$key}' where id=1";
    echo "INSERT SQL: ".$query."<br/>";
    $result = mysql_query($query);
}
//select 出库,并带入查询
$query  = "SELECT * FROM test WHERE id = 1";
$result = mysql_query($query);
$row = mysql_fetch_row($result);
echo "<br/>";
$query  = "SELECT * FROM test WHERE user = '{$row[1]}'";
print_r('SELECT SQL: '.$query.'<br/>');
$result = mysql_query($query)or die('<pre>'.mysql_error().'</pre>');;
echo "<br/>";
print_r(mysql_fetch_row($result));
mysql_close($con);
?>

测试截图:

0x05 全局防护盲点

1、str_replace函数  过滤单引号等,可能造成注入;

2、stripslashes() 函数删除由 addslashes() 函数添加的反斜杠。stripslashes函数使用不当,可能造成注入;

①注入点类似id=1这种整型的参数就会完全无视GPC的过滤;
②注入点包含键值对的,那么这里只检测了value,对key的过滤就没有防护;
③有时候全局的过滤只过滤掉GET、POST和COOKIE,但是没过滤SERVER。

①FILES注入,全局只转义掉GET、POST等传来的参数,遗漏了FILES;
②变量覆盖,危险函数:extract()、parse_str()、$$。

  

0X06 漏洞防护

  基本思路:输入(解决数字型注入)-------转义处理(解决字符型注入)-------输出(解决数据库报错)

1、检查输入的数据是否具有所期望的数据格式。PHP 有很多可以用于检查输入的函数,从简单的变量函数和字符类型函数(比如 is_numeric(),ctype_digit())到复杂的 Perl 兼容正则表达式函数都可以完成这个工作。如果程序等待输入一个数字,可以考虑使用 is_numeric() 来检查,或者直接使用 settype() 来转换它的类型,也可以用 sprintf() 把它格式化为数字。

2、PHP内置转义函数

 

Addslashes()   http://php.net/manual/zh/function.addslashes.php

magic_quote_gpc http://php.net/manual/zh/info.configuration.php#ini.magic-quotes-gpc

mysql_real_escape_string()  http://php.net/manual/zh/function.mysql-real-escape-string.php

mysql_escape_string()      http://php.net/manual/zh/function.mysql-escape-string.php

3、数据库报错信息泄露防范:

  把php.ini文件display_errors = Off

  数据库查询函数前面加一个@字符

 

最有效可预防SQL注入攻击的防御方式:预处理技术进行数据库查询:

代码示例:

<?php
$mysqli = new MySQLi("localhost","root","root","test");
if(!$mysqli){
 die($mysqli->error);
}
$sql = "select id,username,password from users where id=?";////创建一个预定义的对象 ?占位
$mysqli_stmt = $mysqli->prepare($sql);
$id=$_REQUEST['id'];
$mysqli_stmt->bind_param("i",$id);////绑定参数
$mysqli_stmt->bind_result($id,$username,$password);////绑定结果集
$mysqli_stmt->execute();//执行
while($mysqli_stmt->fetch()){   //取出绑定的结果集
 echo $id." ".$username ." ". $password;
}
echo  "<br/>";
echo $sql;
$mysqli_stmt->free_result(); ////关闭结果集
$mysqli_stmt->close();
$mysqli->close();
?>

 

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
目录
相关文章
|
7月前
|
消息中间件 缓存 弹性计算
纯PHP+MySQL手搓高性能论坛系统!代码精简,拒绝臃肿
本内容分享了一套经实战验证的社交系统架构设计,支撑从1到100万用户的发展,并历经6次流量洪峰考验。架构涵盖客户端层(App、小程序、公众号)、接入层(API网关、负载均衡、CDN)、业务服务层(用户、内容、关系、消息等服务)、数据层(MySQL、Redis、MongoDB等)及运维监控层(日志、监控、告警)。核心设计包括数据库分库分表、多级缓存体系、消息队列削峰填谷、CQRS模式与热点数据动态缓存。同时提供应对流量洪峰的弹性伸缩方案及降级熔断机制,并通过Prometheus实现全链路监控。开源建议结构清晰,适合大型社交平台构建与优化。
305 11
|
4月前
|
存储 缓存 安全
php项目源码加密之Zend Guard Loader加密的代码解密可能性很小-优雅草卓伊凡
php项目源码加密之Zend Guard Loader加密的代码解密可能性很小-优雅草卓伊凡
187 2
|
IDE PHP 开发工具
PHP中的类型提示与严格模式:提高代码可维护性
随着PHP语言的发展,开发者对代码的可读性、可维护性和可靠性有了更高的要求。PHP中的类型提示(Type Hinting)和严格模式(Strict Mode)为开发者提供了更强的类型检查机制,有助于提升代码质量和减少潜在的错误,尤其是在大型项目中。
|
安全 程序员 PHP
PHP中的异常处理:提升代码的健壮性
【10月更文挑战第8天】在编程的世界中,错误和异常是不可避免的。它们就像路上的坑洼,可能会让我们的程序“跌倒”。但是,如果我们能够正确地处理这些异常,就可以让我们的程序更加稳健,就像我们学会了如何在坑洼的路上稳稳地行走一样。本文将介绍PHP中的异常处理机制,以及如何使用它来提升我们的代码质量。
|
8月前
|
SQL 缓存 Java
框架源码私享笔记(02)Mybatis核心框架原理 | 一条SQL透析核心组件功能特性
本文详细解构了MyBatis的工作机制,包括解析配置、创建连接、执行SQL、结果封装和关闭连接等步骤。文章还介绍了MyBatis的五大核心功能特性:支持动态SQL、缓存机制(一级和二级缓存)、插件扩展、延迟加载和SQL注解,帮助读者深入了解其高效灵活的设计理念。
|
11月前
|
SQL 存储 关系型数据库
SQL自学笔记(3):SQL里的DCL,DQL都代表什么?
本文介绍了SQL的基础语言类型(DDL、DML、DCL、DQL),并详细说明了如何创建用户和表格,最后推荐了几款适合初学者的免费SQL实践平台。
646 3
SQL自学笔记(3):SQL里的DCL,DQL都代表什么?
|
11月前
|
PHP 开发者 容器
PHP命名空间深度解析:避免命名冲突与提升代码组织####
本文深入探讨了PHP中命名空间的概念、用途及最佳实践,揭示其在解决全局命名冲突、提高代码可维护性方面的重要性。通过生动实例和详尽分析,本文将帮助开发者有效利用命名空间来优化大型项目结构,确保代码的清晰与高效。 ####
177 20
|
供应链 安全 NoSQL
PHP 互斥锁:如何确保代码的线程安全?
在多线程和高并发环境中,确保代码段互斥执行至关重要。本文介绍了 PHP 互斥锁库 `wise-locksmith`,它提供多种锁机制(如文件锁、分布式锁等),有效解决线程安全问题,特别适用于电商平台库存管理等场景。通过 Composer 安装后,开发者可以利用该库确保在高并发下数据的一致性和安全性。
180 6
|
11月前
|
SQL 数据挖掘 数据库
SQL自学笔记(2):如何用SQL做简单的检索
本文深入介绍了SQL的基本语法,包括数据查询、过滤、排序、分组及表连接等操作,并通过实际案例展示了SQL在用户研究中的应用,如用户行为分析、用户细分、用户留存分析及满意度调查数据分析。
202 0
SQL自学笔记(2):如何用SQL做简单的检索
|
11月前
|
SQL 数据挖掘 关系型数据库
SQL自学笔记(1):什么是SQL?有什么用?
本文为用户研究新手介绍SQL(结构化查询语言),解释了SQL的基本概念、入门方法及在用户研究中的应用通过实际案例说明,如用户行为分析、用户细分和满意度调查数据分析,展示了SQL在用户研究中的重要作用。
3864 0
SQL自学笔记(1):什么是SQL?有什么用?