欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

关于SQL注入的防范教程

程序员文章站 2022-04-12 20:20:33
前段时间部门遇到SQL注入攻击,在此,我也分享一下自己的经验和理解。 首先,一个很重要的论点:SQL注入是可以完全杜绝的!SQL注入并不可怕,根本上杜绝的方案很暴力很简单。 S...

前段时间部门遇到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注入。