浅谈PHP代码审计之SQL注入?php的sql注入问题

浅谈PHP代码审计之SQL注入简介
在数据交互中,前端的数据传入到后台处理时,由于后端没有没有做过滤或过滤不严,则会导致其传入的恶意“数据”拼接到SQL语句中,被当作SQL语句的一部

简介

在数据交互中,当前端数据传递到后端进行处理时,后端要么不进行任何过滤,要么过滤不够严格,无法合并收到的恶意“数据”。包含在SQL 语句中并被视为部分执行。此漏洞发生在对数据库执行注入的脚本中。

使用条款:

参数用户可控:前端传递给后端的参数内容是用户可控的。参数被合并到数据库查询中。传递的参数被组合成SQL语句并合并到数据库查询中。

检索关键词

PHP 代码审计允许您捕获一些特定的关键字和代码模式,以发现潜在的SQL 注入漏洞。以下是一些可以指示SQL 注入风险的常用关键字和代码模式。

直接拼接 SQL 语句:

或.=:用于连接字符串的运算符可能表示SQL 语句是直接连接的。 mysql_query:这是一个较旧的MySQL函数,不提供参数绑定,很容易导致SQL注入。 $db-query 或$db-exec:这些是数据库抽象层(例如PDO 或MySQLi)的方法,如果不使用参数绑定也可能导致SQL 注入。

未转义的用户输入:

$_GET、$_POST、$_REQUEST、$_COOKIE:这些超级全局变量直接包含用户输入,如果处理不当可能导致SQL注入。 $_SERVER 中的某些变量(例如$_SERVER[\’PHP_SELF\’])可能包含用户输入,应谨慎对待。

未使用预处理语句:

准备和缺少bindParam或绑定值:这些是准备好的语句中的重要方法,如果您的代码中未使用它们,则可能面临SQL 注入的风险。

未验证的用户输入:

缺少输入验证或过滤函数,例如filter_input、filter_var、preg_match。

特定 SQL 函数和操作符:

SELECT、INSERT、UPDATE、DELETE:这些是SQL 查询中的关键字,如果直接跟在用户输入后面,可能会带来风险。 LIKE:使用LIKE进行模糊查询时,如果通配符处理不当,也可能发生SQL注入。

数据库连接和查询函数:

mysqli_query、PDO:query:这些函数用于执行SQL 查询,如果不使用参数绑定,可能会很危险。 mysqli_real_escape_string, PDO:quote:这些函数用于转义字符串,但它们并不是防止SQL 注入的最佳方法,因为它们不能防止所有类型的注入。

动态构建 SQL 语句:

如果未正确处理用户输入,则使用if 和switch 等条件语句动态构造SQL 语句可能会很危险。

错误处理:

如果您的代码中有显示数据库错误消息的逻辑,则可能有助于攻击者发现注入点。

无过滤

数字型

$user_id=$_GET[\’id\’] //用户输入的ID

$sql=\’SELECT * FROM users WHERE id=$user_id\’;

$结果=mysqli_query($conn, $sql);

字符型

$user_id=$_GET[\’id\’] //用户输入的ID

$sql=\’SELECT * FROM users WHERE id=\’$user_id\’\’;

$结果=mysqli_query($conn, $sql);

搜索型

$search_term=$_GET[\’search\’] //搜索用户输入的关键字。

$sql=\’从名称类似\’%$search_term%\’的产品中选择*;

$结果=mysqli_query($conn, $sql);

存在过滤或过滤不严谨

magic_quotes_gpc

magic_quotes_gpc是PHP早期版本中的一个配置选项,可以自动转义从GET、POST和COOKIE获取的数据,以防止SQL注入和跨站脚本攻击等安全问题。

如果启用了magic_quotes_gpc (magic_quotes_gpc=On),PHP 会自动添加反斜杠(\\) 来转义这些特殊字符。

例如:

如果用户发送带单引号的字符串A\’apple

然后PHP会自动将其转换为A\\\’apple

配置选项文件名:php.ini

但是,magic_quotes_gpc 在PHP 5.4.0 中已弃用,并在PHP 7.0.0 中删除。

这是因为magic_quotes_gpc 有几个问题:

它会自动转义数据,这可能会破坏数据完整性,但可能没有必要。它只是转义特殊字符,而不严格验证或过滤数据,这可能会导致安全问题。无论数据是否需要转义,它都会转义所有GET、POST 和COOKIE 数据,这可能会导致性能问题。

在日常程序开发过程中,我们经常会判断magic_quotes_gpc是否开启,如果没有开启,则使用addslashes函数来过滤输入的参数值。 SQL注入风险。

常见过滤函数

addslashes():在特殊字符(单引号、双引号、反斜杠和NULL 字符)前添加反斜杠。

htmlspecialchars():将特殊字符(,\’,\’,) 转换为HTML 实体。

htmlentities():将所有可转换字符转换为HTML 实体。

strip_tags():从字符串中删除HTML 和PHP 标签。

filter_var():使用过滤器过滤变量。

mysqli_real_escape_string():转义SQL 语句中的特殊字符。

PDO:quote():转义SQL 语句中的特殊字符。

过滤函数缺陷

addslashes()

addslashes()函数可以过滤传入参数中的特殊字符,但如果执行的SQL语句是数字类型则不需要关闭,从而允许注入的恶意代码直接执行。

$user_id=addlashes($_GET[\’id\’]) //用户输入的ID

$sql=\’SELECT * FROM users WHERE id=$user_id\’;

$结果=mysqli_query($conn, $sql);

htmlspecialchars()

htmlspecialchars()用于将特殊字符转换为HTML实体,例如转换为lt、转换为gt;该功能不是SQL注入,主要用于防止XSS(跨站脚本)攻击。 因此,它无法过滤特殊字符或防止SQL注入。

$user_id=htmlspecialchars($_GET[\’id\’]); //用户输入的ID

$sql=\’SELECT * FROM users WHERE id=$user_id\’;

$结果=mysqli_query($conn, $sql);

字符集不匹配导致单引号的逃逸

在SQL 注入攻击中,攻击者通常会尝试通过在输入中插入特殊字符(例如单引号\’)来破坏SQL 查询的结构。为了防止这种情况,应用程序通常会对这些特殊字符进行转义。然而,如果转义处理不当,特别是在处理宽字符时,攻击者可能能够绕过转义并通过添加某些字符序列来引起注入。

原因:如果应用程序字符集与数据库连接字符集不匹配,则可能发生宽字节注入。

例如,如果您的应用程序使用UTF-8 编码,但数据库连接配置为使用单字节字符集(例如latin1),则在将用户输入传递到数据库之前,宽字符可能无法正确编码。处理。

set character_set_client = gbk

虽然设置Character_set_client=gbk本身并不会直接导致SQL注入,但是它会增加SQL注入的风险,特别是在处理GBK等多字节字符集时。此设置可能会影响应用程序处理用户输入的方式,尤其是字符编码转换和SQL 查询构造。

如果character_set_client设置为gbk,则MySQL服务器期望客户端发送的数据是GBK编码的。如果您的应用程序的默认编码不是GBK,则在将用户输入发送到数据库之前可能需要进行编码转换。如果这个转换过程处理不当,可能会带来SQL 注入的风险。

$conn=mysql_connect(\’localhost\’, \’用户名\’, \’密码\’);

mysql_select_db(\’数据库\’, $conn);

方法一

mysql_query(\’设置名称\’gbk\’\’, $conn);

方法2

mysql_set_charset(\’gbk\’, $conn);

或者

setcharacter_set_client=gbk

漏洞示例

假设您的PHP应用程序的默认编码是UTF-8,但您的数据库连接设置为character_set_client=gbk。如果应用程序将UTF-8 编码的用户输入直接发送到数据库而没有对其进行正确转换,则可能会导致字符解析错误,并可利用该错误进行SQL 注入。

$username=$_POST[\’username\’]; //用户输入假设采用UTF-8 编码

$sql=\’SELECT * FROM users WHERE username=\’$username\’\’;

mysqli_query($conn, $sql);

iconv()

iconv() 是一个PHP 函数,用于在不同字符编码之间转换字符串。当使用多语言网站或需要与不同编码的数据库或系统交互时,此功能特别有用。

使用方法

$converted=iconv(from_encoding, to_encoding, $string);

from_encoding:源字符编码。 to_encoding:目标字符编码。 $string:需要转换的字符串。

漏洞示例

考虑一个PHP 应用程序,它使用iconv() 函数将用户输入从一种编码转换为另一种编码,然后直接在SQL 查询中使用转换后的字符串。如果此转换过程不能正确处理特殊字符,则可能导致SQL 注入漏洞。

$用户名=$_POST[\’用户名\’];

$username=iconv(\’UTF-8\’, \’gbk\’, $username); //假设这是一个错误的转换。

$sql=\’SELECT * FROM users WHERE username=\’$username\’\’;

$结果=mysqli_query($conn, $sql);

mb_convert_encoding()

mb_convert_encoding() 是PHP 中的多字节字符处理函数,用于将字符串从一种字符编码转换为另一种字符编码。该函数特别适合处理包含多字节字符集(UTF-8、GBK、Shift-JIS 等)的字符串。

使用方法

字符串mb_convert_encoding ( 字符串$str , 字符串$to_encoding [,mixed $from_encoding=mb_internal_encoding() ] )

$str:要编码的字符串。 $to_encoding:目标编码。 $from_encoding:可选参数。指定源编码。可以是编码名称字符串或编码名称数组。如果未指定,则使用mb_internal_encoding() 返回的编码。

漏洞示例

考虑一个PHP 应用程序,它使用mb_convert_encoding() 函数将用户输入从一种编码转换为另一种编码,并直接在SQL 查询中使用转换后的字符串。如果此转换过程不能正确处理特殊字符,则可能导致SQL 注入漏洞。

$用户名=$_POST[\’用户名\’];

$username=mb_convert_encoding($username, \’UTF-8\’, \’gbk\’) //假设这是一个错误的转换。

$sql=\’SELECT * FROM users WHERE username=\’$username\’\’;

$结果=mysqli_query($conn, $sql);

解码函数不恰当的使用导致的注入

urldecode()

urldecode() 是一个PHP 内置函数,用于解码编码的URL 字符串。 URL 编码是将特殊字符转换为可以在URL 中安全发送的格式的过程。 urldecode() 函数将这些编码字符转换为其原始格式。

使用方法

字符串url 解码(字符串$str )

$str:要解码的URL 编码字符串。

urldecode() 函数在PHP 中用于解码编码的URL 字符串,尽管它本身并不直接导致SQL 注入,特别是在处理用户输入和构建SQL 查询时如果使用不当可能会发生。 SQL注入。

漏洞示例

考虑一个PHP 应用程序,它从URL 参数检索用户名,使用urldecode() 函数对参数进行解码,并直接在SQL 查询中使用解码后的字符串。

$用户名=urldecode($_GET[\’用户名\’]);

$sql=\’SELECT * FROM users WHERE username=\’$username\’\’;

$结果=mysqli_query($conn, $sql);

base64_decode()

Base64_decode()是一个PHP内置函数,用于解码base64编码的字符串。 Base64 编码是一种将二进制数据转换为ASCII 字符串的方法,通常用于通过不支持二进制传输的媒体传输数据。

使用方法

字符串base64_decode(字符串$encoded_string)

$encoded_string:要解码的Base64 编码字符串。

漏洞示例

考虑一个PHP 应用程序,它从用户输入中获取Base64 编码的字符串,使用base64_decode() 函数对字符串进行解码,然后直接在SQL 查询中使用解码后的字符串。

$encoded_username=$_GET[\’用户名\’];

$用户名=base64_decode($encoded_username);

$sql=\’SELECT * FROM users WHERE username=\’$username\’\’;

$结果=mysqli_query($conn, $sql);

json_encode()

json_encode() 是一个内置的PHP 函数,用于将PHP 数据转换为JSON 格式。 JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,易于人类阅读和编写,也易于机器解析和生成。

使用方法

字符串json_encode (混合$value [, int $options=0 [, int $Depth=512 ]] )

$value:要编码的值。该函数仅对数组或对象有效。 $options:用于设置编码选项的可选参数。您可以使用以下常量的任意组合:

JSON_HEX_TAGJSON_HEX_AMPJSON_HEX_APOSJSON_HEX_QUOTJSON_FORCE_OBJECTJSON_NUMERIC_CHECKJSON_PRETTY_PRINTJSON_UNESCAPED_SLASHESJSON_UNESCAPED_UNICODE 等

$Depth:可选参数,指定最大深度。必须大于0。

漏洞示例

假设您有一个接收用户输入并将其存储在数据库中的应用程序。如果用户输入的数据在保存并直接用于构造SQL 查询之前经过json_encode() 处理,则可能会发生SQL 注入。

$user_input=$_POST[\’data\’] //用户输入的数据

$json_data=json_encode($user_input); //将用户输入转换为JSON。

//假设这是数据库插入代码。

$sql=\’插入用户(数据)值(\’$json_data\’)\’;

$结果=mysqli_query($conn, $sql);

弱类型函数不恰当的使用导致的注入

intval()

intval() 是PHP 内置函数,用于将变量转换为整数类型。当您需要确保变量是整数时(例如在数据库查询中使用ID 或其他数字参数时),此函数特别有用。

使用方法

int intval (混合$var [, int $base=10 ] )

$var:要转换的变量。 $base:可选参数。用于指定转换的基础。默认值为10。

$var=\’1234\’;

$int=intval($var);

回声$int; //输出:1234

$var=\’1234abc\’;

$int=intval($var);

回声$int; //输出:1234

$var=\’abc1234\’;

$int=intval($var);

回声$int; //输出:0

$var=\’1234abc5678\’;

$int=intval($var);

回声$int; //输出:1234

$var=\’0x1A\’ //十六进制数

$int=intval($var, 0);

回声$int; //输出:26

$var=\’0123\’; //八进制数

$int=intval($var, 8);

回声$int; //输出:83

漏洞示例

假设您有一个接收用户输入并将其存储在数据库中的应用程序。如果用户输入的数据在保存并直接用于构造SQL 查询之前经过intval() 处理,则可能会发生SQL 注入。

$id=isset($_GET[\’id\’])?$_GET[\’id\’]:1;

if(intval($id)99){

die(\’条件不满足\’);

}

$sql=\’从用户中选择* WHERE id=$id\’;

$结果=mysqli_query($conn, $sql);

is_numeric()

is_numeric() 是一个内置的PHP 函数,用于检查变量是数字还是数字字符串。这个函数非常有用,特别是当你需要确保用户输入或外部数据可以被安全地视为数字时。

使用方法

bool is_numeric(混合$var)

$var:要检查的变量。

返回值:

如果变量是数字或数字字符串,则返回true。如果变量不是数字或数字字符串,则返回false。

$var1=123;

$var2=\’123\’;

$var3=\’123abc\’;

$var4=\’abc123\’;

$var5=12.34;

$var6=\’12.34\’;

$var7=\’0x1A\’ //十六进制数

$var8=\’0123\’; //八进制数

var_dump(is_numeric($var1)); //输出:bool(true)

var_dump(is_numeric($var2)); //输出:bool(true)

var_dump(is_numeric($var3)); //输出:bool(true)

var_dump(is_numeric($var4)); //输出:bool(false)

var_dump(is_numeric($var5)); //输出:bool(true)

var_dump(is_numeric($var6)); //输出:bool(true)

var_dump(is_numeric($var7)); //输出:bool(true)

var_dump(is_numeric($var8)); //输出:bool(true)

漏洞示例

假设您有一个接收用户输入并将其存储在数据库中的应用程序。当用户输入的数据在保存并直接用于构建SQL 查询之前由is_numeric() 处理时,可能会发生SQL 注入。

$id=$_GET[\’id\’] //用户输入的数据

如果(is_numeric($id)){

$sql=\’SELECT * FROM users WHERE id=$id\’ //假设这是您的数据库查询代码。

$结果=mysqli_query($conn, $sql);

}

in_array()

in_array() 是一个内置PHP 函数,用于检查数组中是否存在某个值。这个函数非常有用,特别是当您需要检查数组中是否包含某个值时。

使用方法

bool in_array (混合$needle , 数组$haystack [, bool $strict=FALSE ] )

$needle:要在数组中搜索的值。 $haystack:要搜索的数组。 $strict:可选参数。当设置为true 时,in_array() 在比较时检查数据类型。也就是说,不同类型的值,例如字符串和整数,不被视为匹配。默认值为false。

返回值:

如果找到$needle,则返回true。如果未找到$needle,则返回false。

漏洞示例

假设您有一个应用程序,它接受用户输入并使用它来构建SQL 查询。如果用户输入的数据在用于SQL 查询之前未得到正确处理,即使使用in_array(),也可能发生SQL 注入。

$user_inp

ut = $_GET[\’fruit\’]; // 用户输入的数据
$allowed_fruits = array(\”apple\”, \”banana\”, \”cherry\”);
if (in_array($user_input, $allowed_fruits)) {
$sql = \”SELECT * FROM fruits WHERE name = \’$user_input\’\”;
$result = mysqli_query($conn, $sql);
}

array_search()

array_search() 是 PHP 中的一个内置函数,用于在数组中搜索给定的值,并返回它的键。如果找到多个匹配项,array_search() 将只返回第一个匹配项的键。

使用方法

mixed array_search ( mixed $needle , array $haystack [, bool $strict = false ] )
$needle:要在数组中搜索的值。$haystack:要搜索的数组。$strict:可选参数,如果设置为 true,array_search() 将在比较时检查数据类型,这意味着字符串和整数等不同类型的值不会被认为是匹配的。默认值为 false。
返回值:

如果找到 $needle,则返回它的键(索引)。如果未找到 $needle,则返回 false。

漏洞示例

假设你有一个应用程序,它接收用户输入并将其用于构建 SQL 查询。如果用户输入的数据在用于 SQL 查询之前没有被正确处理,那么即使使用了array_search(),也可能发生 SQL 注入

$user_input = $_GET[\’fruit\’]; // 用户输入的数据
$allowed_fruits = array(\”apple\”, \”banana\”, \”cherry\”);
if (array_search($user_input, $allowed_fruits)) {
$sql = \”SELECT * FROM fruits WHERE name = \’$user_input\’\”;
$result = mysqli_query($conn, $sql);
}

switch

在 PHP 中,switch 语句是一种流程控制结构,用于根据不同的条件执行不同的代码块。switch 语句通常用于替代多个 if…else 语句,使代码更加清晰和易读。

使用方法

switch (expression) {
case value1:
// 当 expression 等于 value1 时执行的代码
break;
case value2:
// 当 expression 等于 value2 时执行的代码
break;
// 更多 case 语句…
default:
// 当 expression 不匹配任何 case 时执行的代码
}
expression:要进行比较的表达式。case:定义不同的值,expression 将与这些值进行比较。break:在执行完一个 case 后,break 语句用于跳出 switch 语句,防止执行后续的 case。default:可选,当 expression 不匹配任何 case 时执行的代码块。

漏洞示例

假设你有一个应用程序,它接收用户输入并根据输入的类型执行不同的 SQL 查询。如果用户输入的数据在用于 SQL 查询之前没有被正确处理,那么即使使用了 switch 语句,也可能发生 SQL 注入。

$user_input = $_GET[\’type\’]; // 用户输入的数据
switch ($user_input) {
case \’get_user\’:
$sql = \”SELECT * FROM users WHERE username = \’$user_input\’\”;
break;
case \’get_product\’:
$sql = \”SELECT * FROM products WHERE name = \’$user_input\’\”;
break;
// 更多 case 语句…
default:
echo \”Invalid input!\”;
}
$result = mysqli_query($conn, $sql);

==

=(赋值运算符):

= 是赋值运算符,用于将右边的值赋给左边的变量。例如:$x = 10; 表示将数值 10 赋给变量 $x。
==(等于运算符):

== 是比较运算符,用于检查两个值是否相等,但不考虑它们的类型。如果两个值在松散比较下相等,则返回 true;否则返回 false。例如:$x == $y 如果 $x 和 $y 的值相等,则返回 true,即使它们的类型不同。例如,1 == \’1\’ 返回 true。
===(全等于运算符):

=== 是比较运算符,用于检查两个值是否相等,并且它们的类型也相同。如果两个值在严格比较下相等(即值和类型都相等),则返回 true;否则返回 false。例如:$x === $y 如果 $x 和 $y 的值和类型都相等,则返回 true。例如,1 === \’1\’ 返回 false,因为虽然值相等,但类型不同(一个是整数,一个是字符串)。

使用方法

$x = 10; // 赋值
$y = \’10\’;
if ($x == $y) {
echo \’$x 和 $y 在松散比较下相等<br>\’;
}
if ($x === $y) {
echo \’$x 和 $y 在严格比较下相等<br>\’;
} else {
echo \’$x 和 $y 在严格比较下不相等<br>\’;
}

漏洞示例

假设你有一个应用程序,它接收用户输入并根据输入的类型执行不同的 SQL 查询。如果用户输入的数据在用于 SQL 查询之前没有被正确处理,那么即使使用了 == 进行比较,也可能发生 SQL 注入。

$user_input = $_GET[\’type\’]; // 用户输入的数据
if ($user_input == \’get_user\’) {
$sql = \”SELECT * FROM users WHERE username = \’$user_input\’\”;
} elseif ($user_input == \’get_product\’) {
$sql = \”SELECT * FROM products WHERE name = \’$user_input\’\”;
} else {
echo \”Invalid input!\”;
}
$result = mysqli_query($conn, $sql);

常被忽略的http信息传入导致的注入

HTTP 头部 SQL 注入是一种特殊类型的 SQL 注入攻击,它发生在应用程序从 HTTP 头部(如 Cookie、User-Agent、Referer 等)中获取数据,并将其直接用于构造 SQL 查询时,没有对这些数据进行适当的验证和转义。 最根本原因,是因为程序员会设置全局过滤防止SQL注入,但开启全局过滤只会对GET,POST,COOKIE这些传入的值都会进行过滤。而对于http的头部信息来说,是无法直接获取的,需要通过 $_SERVER 或 getenv() 函数进行获取。

例如:获取ip 可以使用 getenv(\’HTTP_X_FORWARDED_FOR\’)或$_SERVER[\’HTTP_X_FORWARDED_FOR\’]

以下是一些常用的 HTTP 请求头信息及其在 $_SERVER 中的对应键名:

\’HTTP_HOST\’:请求头中的 Host 字段。
\’HTTP_USER_AGENT\’:请求头中的 User-Agent 字段,通常包含浏览器的名称和版本。
\’HTTP_ACCEPT\’:请求头中的 Accept 字段,指定客户端可以接受的内容类型。
\’HTTP_ACCEPT_LANGUAGE\’:请求头中的 Accept-Language 字段,指定客户端偏好的语言。
\’HTTP_ACCEPT_ENCODING\’:请求头中的 Accept-Encoding 字段,指定客户端支持的内容编码。
\’HTTP_CONNECTION\’:请求头中的 Connection 字段,指定连接的类型,如 keep-alive。
\’HTTP_COOKIE\’:请求头中的 Cookie 字段,包含客户端发送的 cookie 信息。
\’HTTP_REFERER\’:请求头中的 Referer 字段,指定链接到当前页面的前一页面的 URL。

漏洞示例

下面是一个 HTTP 头部 SQL 注入的示例,其中使用了用户代理(User-Agent)头部来构建 SQL 查询:

$user_agent = $_SERVER[\’HTTP_USER_AGENT\’]; //获取客户端浏览器信息
$query = \”SELECT * FROM users WHERE user_agent = \’$user_agent\’\”;
$result = mysqli_query($conn, $sql); // 执行 SQL 查询
#以上关于浅谈PHP代码审计之SQL注入的相关内容来源网络仅供参考,相关信息请以官方公告为准!

原创文章,作者:CSDN,如若转载,请注明出处:https://www.sudun.com/ask/92402.html

(0)
CSDN's avatarCSDN
上一篇 2024年6月26日 下午11:51
下一篇 2024年6月27日 上午1:04

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注