关于SQL注入的防范教程

  • 2022-07-05 22:53:16

前段时间部门遇到SQL注入攻击,在此,我也分享一下自己的经验和理解。

首先,一个很重要的论点:SQL注入是可以完全杜绝的!SQL注入并不可怕,根本上杜绝的方案很暴力很简单。

SQL注入原因

通俗点讲,SQL注入的根本原因是: "用户输入数据"意外变成了代码被执行。

"用户输入数据"我这里可以指Web前端$_POST,$_GET获取的数据,也可以指从数据库获取的数据,当然也不排除程序猿无意中使用的特殊字符串。

在SQL语句的拼接中,一些含特殊字符的变量在拼接时破坏了SQL语句的结构,导致"用户输入数据"意外变成了代码被执行。

SQL语句拼接

PHP语言为例,PHP中SQL拼接方式主要有以下几种

1. 数值类型 直接拼接

例如

$sql = 'SELECT * from table where id = '. $id

$sql = "SELECT * from table where id = $id ";

这里开发者默认将$id当做数值类型,进行直接拼接。调试时,当$id为字符串时会报错,所以可以提前发现拼接错误。通常会用intval($id),floatval($id)强制转换成数值型,并不会造成SQL注入。

2. 字符串型拼接

例如

$sql = 'SELECT * from table where name = '."$name"

$sql = "SELECT * from table where name = '$name' ";

这里开发者默认将$name当做字符串处理。当$name中含有单引号(')或 双引号("),会吃掉$name变量上的单引号或双引号,从而破坏了$sql的结构使得$name有可能变成SQL命令被执行,这也是为什么一些老函数addslashes()可以防止简单的SQL注入。

除了$name直接含有单/双引号('/")外,由于一些特殊的编码(如特定的汉子字符)也可以吃掉$name变量上的单引号或双引号,所以addslashes()、mysql_escape_string()、mysql_real_escape_string()这种转义特殊字符的方法是有风险的。

但无论编码情况多么复杂,SQL注入有一点是不变的: 吃掉 单/双引号,从而破坏SQL语句结构。理解这一点,可以指导开发者编码,从而加强开发者对SQL注入风险的控制能力。

防止SQL注入方法

在此我把方法归纳为3类。

1.字符(串)过滤法

通常过滤字符串变量中一些敏感词和字符,如" '|and|exec|insert|select|delete|update|count|*|%|chr|mid|master|truncate|char|declare|;|or|-|+|, "等,或者用正则表达式过滤传入的参数。

个人觉得字符(串)过滤法并不适合防止SQL注入,因为字符(串)过滤对防止破坏SQL语句结构毫无用处(过滤单/双引号除外),字符(串)过滤法更侧重SQL语句结构破坏后,阻止侵入者执行他的代码意图。(破坏入侵者的代码意图,实际上自己的SQL语句结构已经被破坏了)。

2.字符转义法 (不推荐使用)

addslashes()

addslashes()是在 单引号(')、双引号(")、反斜线(\)与 NUL(NULL 字符)前加上了反斜线,但是遇到一些特殊编码也无能为力。如:含0xbf27 的字就不会,所以 select * from table where name='縗' OR 1 就会参数SQL注入。在上面的SQL拼接中 $sql = " SELECT * from table where name = '$name' "中,如果$name = "'縗' or 1",用$name = addslashes($name) 还是会产生SQL注入.

mysql_escape_string()、mysql_real_escape_string() (不推荐使用,且只适用于MySQL)

mysql_escape_string 和 mysql_real_escape_string功能一样,都是在以下字符前添加反斜杠: \x00, \n, \r, \, ', " 和 \x1a。mysql_real_escape_string使用之前要先连接上数据库,会考虑当前链接connection字符集。

字符转义法并不能完全避免SQL注入风险,应谨慎使用。

由于一些复杂的业务逻辑,无法完全避免SQL语句拼接,在变量可控的情况下,使用这种字符转义法也未尝不可,比不使用直接拼接要强得多。

3.预处理语句法

PDO

$pdo = new PDO('mysql:dbname=dbtest;host=127.0.0.1;charset=utf8', 'user', 'pass');

$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$stmt = $pdo->prepare('SELECT * FROM employees WHERE name = :name');

$stmt->execute(array('name' => $name));

foreach ($stmt as $row) {

}

MYSQLi

$stmt = $dbConnection->prepare('SELECT * FROM employees WHERE name = ');

$stmt->bind_param('s', $name);

$stmt->execute();

$result = $stmt->get_result();

while ($row = $result->fetch_assoc()) {

}

预处理语句方法会先解析SQL语句,然后通过传入不同的参数值来执行SQL,不需要每次执行都解析SQL语句,所以处理效率也较高;预处理语句在SQL语句解析协议上避免将参数当做SQL命令执行,仅仅当做值传递,所以可以完全避免SQL注入。

猜你喜欢