PHP MySQL防注入

转载自 https://blog.csdn.net/zhouyuqi1/article/details/82253302

本文将从sql注入风险说起,并且比较addslashesmysql_escape_stringmysql_real_escape_string、mysqli和pdo的预处理的区别。

当一个变量从表单传入到php,需要查询mysql的话,需要进行处理。
举例:

$unsafe_variable = $_POST['user_input']; 
mysqli_query("INSERT INTO table (column) VALUES ('" . $unsafe_variable . "')"); 

用户可以输入诸如 :value'); DROP TABLE table;--,SQL语句就变成这样了:

INSERT INTO table (column) VALUES('value'); DROP TABLE table;--') 

执行的结果就是table表被删掉了。

这是一种常见的sql注入方法,那么在程序中,应该怎样预防呢?

1.魔术引用 (推荐指数3)
addslashes()与stripslashes()是功能相反的函数。
addslashes()用于对变量中的' " 和NULL添加斜杠,用于避免传入sql语句的参数格式错误,同时如果有人注入子查询,通过加可以将参数解释为内容,而非执行语句,避免被mysql执行。

不过,addslashes()添加的只在php中使用,并不会写入mysql中。
那么,tripslashes()的作用是将加了的php变量去掉,由于不会写入mysql中,所以从mysql查询出来的内容不需要再tripslashes()。

在防注入方面,addslashes()可以防止掉大多数的注入,但是此函数并不会检查变量的编码,当使用例如中文gbk的时候,由于长度比较长 ,会将某些gbk编码解释成两个ascii编码,造成新的注入风险(俗称宽字节注入)。见下面2。

如果从网页表单、php、mysql都使用utf8编码,则没有这个问题。
基于此函数的风险,并不建议使用,推荐使用下面3中的方法。
https://segmentfault.com/q/10...

2. mysql_real_escape_string() (推荐指数4)
由于addslashes()不检测字符集,所以有宽字节注入风险,所以php中添加了这个函数。
这个函数本来是mysql的扩展,但是由于存在宽字节的问题,php基于mysql的扩展开发了此函数。

gbk宽字符漏洞导致的sql注入
https://www.91ri.org/8611.html
http://www.cnblogs.com/suihui...

mysql_real_escape_chars()是mysql_escape_chars()的替代用法。
与addslashes()相比,不仅会将' " NOL(ascii的0)转义,还会把r n进行转义。同时会检测数据编码。
按php官方的描述,此函数可以安全的用于mysql。

此函数在使用时会使用于数据库连接(因为要检测字符集),并根据不同的字符集做不同的操作。如果当前连接不存在,刚会使用上一次的连接。

mysql_real_escape_string()防注入详解
此方法在php5.5后不被建议使用,在php7中废除。
参考:https://segmentfault.com/q/10...

3.预处理查询 (Prepared Statements) (推荐指数5)
使用prepared statements(预处理语句)和参数化的查询,可以有效的防止sql注入。

为什么预处理和参数化查询可以防止sql注入呢?
在传统的写法中,sql查询语句在程序中拼接,防注入(加斜杠)是在php中处理的,然后就发语句发送到mysql中,mysql其实没有太好的办法对传进来的语句判断哪些是正常的,哪些是恶意的,所以直接查询的方法都有被注入的风险。
在mysql5.1后,提供了类似于jdbc的预处理-参数化查询。它的查询方法是:

  1. 先预发送一个sql模板过去
  2. 再向mysql发送需要查询的参数
    就好像填空题一样,不管参数怎么注入,mysql都能知道这是变量,不会做语义解析,起到防注入的效果,这是在mysql中完成的。

参考:
PHP中如何防止SQL注入
http://blog.csdn.net/sky_zhe/...

参数化查询为什么能够防止SQL注入
http://www.cnblogs.com/LoveJe...

上面提供的资料比较多,下面根据自己的理解整理出来。

预处理分为两种:
A.使用mysqli:prepare()实现
看一个完整的用法:

$mysqli = new mysqli("example.com", "user", "password", "database");
$stmt = $mysqli->prepare("SELECT id, label FROM test WHERE id = ?");
$stmt->bind_param(1, $city);
$stmt->execute();
$res = $stmt->get_result();
$row = $res->fetch_assoc();

a.写sql语句,然后用?占位符替代sql中的变量
b.替换变量
c.执行
d.得到一个二进制结果集,从二进制结果中取出php结果集
e.遍历结果集

使用预处理,一条查询分两步,所以很安全。也是php5.5及php7推荐方法。
参考:
http://www.cnblogs.com/liuzha...

B. 使用pdo实现
pdo是一个php官方推荐的数据库抽象层,提供了很多实用的工具。

使用pdo的预处理-参数化查询可以有效防止sql注入。
使用方法跟上面差不多,区别在于pdo提供了更多样的方法。
使用这个pdo->$stmt对象进行查询后,会被结果集覆盖,类型是一个二维数组。

我们在上面预处理-参数化查询是在mysql中进行防注入操作的,其实pdo也内置了一个预处理的模拟器,叫做ATTR_EMULATE_PREPARES。
默认情况下,PDO会使用DSN中指定的字符集对输入参数进行本地转义(PHP手册中称为native prepared statements),然后拼接成完整的SQL语句,发送给MySQL Server。这有些像我们平时程序中拼接变量到SQL再执行查询的形式。

这种情况下,PDO驱动能否正确转义输入参数,是拦截SQL注入的关键。然而PHP 5.3.6及老版本,并不支持在DSN中定义charset属性(会忽略之),这时如果使用PDO的本地转义,仍然可能导致SQL注入,

如果ATTR_EMULATE_PREPARES=true(默认情况),预处理-参数化查询在pdo的模拟器中完成,模拟器根据字符集(dsn参数)进行处理,然后把语句发送给mysql。

如果ATTR_EMULATE_PREPARES=false,sql会分两次把参数给送给mysql,mysql根据自身的字符集(set names <charset>)进行处理,完成查询。

但由于各版本差异,pdo在各版本中的实现程度也不一样,有些版本还有bug,我们以php5.3.6做为分界线来进行说明:

php5.3.6以下版本

$pdo = new PDO("mysql:host=localhost;dbname=test;",'root','pwd');
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES,false);
$pdo->exec('set names utf8');
$id = '0 or 1 =1 order by id desc';
$sql = "select * from article where id = ?";
$statement = $pdo->prepare($sql);
$statement->bindParam(1, $id);
$statement->execute();

如上,我们关闭了本地预处理模拟器,参数会直接分批发送给mysql,由mysql根据set name utf8字符集进行检测,完成sql注入处理。以上代码不会产生注入。

php5.3.6以上版本

$pdo = new PDO("mysql:host=localhost;dbname=test;charset=utf8",'root','pwd');
$pdo->exec('set names utf8');
$id = '0 or 1 =1 order by id desc';
$sql = "select * from article where id = ?";
$statement = $pdo->prepare($sql);
$statement->bindParam(1, $id);
$statement->execute();

在php5.3.6以上版本中,默认情况下ATTR_EMULATE_PREPARES开启,模拟器会根据new PDO()中的charset=utf8进行检测,在模拟器上完成防注入操作。如果把模拟器关闭,也会像低版本一样送交mysql进行防注入处理。

参考:
PDO防注入原理分析以及使用PDO的注意事项
http://zhangxugg-163-com.itey...

PHP 5.3.6及以前版本的PDO的bindParam,bindValue潜在的安全隐患
http://zhangxugg-163-com.itey...

再论php 5.3.6以前版本中的PDO SQL注入漏洞问题
http://my.oschina.net/zxu/blo...

segmentfault讨论
https://segmentfault.com/q/10...

  1. html输出与防止xss注入
    特殊字符输出

比如' " < >有着特殊的意义,如果直接写到html中输出,会引起dom格式的错乱,那么就需要用到特殊的输出方法。

htmlspecialchars()
用于将一些特殊符号转义成只有浏览器识别的转义符。

举个例子:

$a = " ' ";
<input type="test" value='<?=$a;?>'>
<textarea><?=$a;?></textarea>

上面由于$a的值就是一个' ,当它输出在value=''之间时,会破坏html原有的dom格式,导致html解析错误。
下面那个'输出在标签对之间时没有问题。
上面那个问题怎么解决呢? 可以这样:

<input type="test" value='<?php echo htmlspecialchars($a);?>'>

php会向浏览器输出:

<input type="test" value='&qouts;'>

这个符号只有流量器认识,源码中看到是这样,但是浏览器输出的就是一个'号。

xss注入
xss也就是常说的跨域攻击,这是一种在客户端浏览器上面执行的攻击。
比如在表单或者url参数中,人为写入javascript代码,看起来是普通的文字,但是被浏览器解析后变成可执行的javascript动作,用来做广告或者攻击等等。

举例:
有人在发贴的时候写入了javascript代码,一打开就弹窗口。

<script type="text/javascript">alert("你是我的小苹果!");</scirpt>

对于这种恶意的东西,为了力求安全,我们即可以在发贴前对可用的html代码进行过滤,也可以用htmlspecialchars()进行转义。
转义后alert()内容变成:

alert("你是我的小苹果!");

虽然看到的文字不变,但是由于转义了,这个alert()只会以文字显示,而不会执行弹窗。

http://netsecurity.51cto.com/...

总结:
1.建议将升级到php 5.3.9+ php 5.4+,php 5.3.8存在致命的hash碰撞漏洞。
2.不要使用基础的php拼接sql语句直接查询,避免使用addslashes()和mysql_real_escape_string()防止注入。
3.推荐使用mysqli或pdo的预处理-参数化查询。
4.pdo默认会使用自带的ATTR_EMULATE_PREPARES模拟器处理sql语句,php5.3.6以下版本使用预处理-参数化时,设置dsn参数(setcahrs=utf8)没用的(可能是bug),所以防注入还是有缺陷。应将模拟器关闭,pdo会把sql交由mysql进行防注入。
5.php5.3.6以上版本,应设置dsn参数(setchars=utf8),默认就可以在模拟器防注入。也可以手工关掉模拟器,效果跟上面第4步一样。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容