开发过程中需要对用户的输入进行转义,不论是安全地显示用户在表单提交中输入的数据,还是在处理 sql 语句时,进行安全地转义可以有效避免跨站脚本攻击(XSS)和 SQL 注入。
1. 使用 htmlentities() 和 htmlspecialchars()
在处理用户提交的表单数据时,先将用户的输入进行转义,再显示在页面上。
可以使用 htmlentities() 和 htmlspecialchars() 函数将特殊字符转换成 HTML 实体(例如把 < 转换成 <)。最基本的是 htmlspecialchars(),可以转义 4 个字符: < > " 和 &(可以根据可选的参数决定转义的字符)。对于更复杂的编码,需要使用 htmlentities(),可以对任何 HTML 实体进行转换。
例:
<?php
$html = "<script>alert('<a href=\"http://baidu.com?user=dee&browser=chrome\">baidu.com</a>');</script>";
echo $html,PHP_EOL; //会弹出alert提示框
//浏览器右键查看源代码
//<script>alert('<a href="http://baidu.com?user=dee&browser=chrome">baidu.com</a>');</script>
echo htmlentities($html),PHP_EOL;
//浏览器右键查看源代码
//<script>alert('<a href="http://baidu.com?user=dee&browser=chrome">baidu.com</a>');</script>
echo htmlspecialchars($html),PHP_EOL; //转义双引号 < > &
//浏览器右键查看源代码
//<script>alert('<a href="http://baidu.com?user=dee&browser=chrome">baidu.com</a>');</script>
echo htmlspecialchars($html, ENT_QUOTES),PHP_EOL; //转义双引号和单引号 < > &
//浏览器右键查看源代码
//<script>alert('<a href="http://baidu.com?user=dee&browser=chrome">baidu.com</a>');</script>
echo htmlspecialchars($html, ENT_NOQUOTES),PHP_EOL; //非单引号和双引号
//浏览器右键查看源代码
//<script>alert('<a href="http://baidu.com?user=dee&browser=chrome">baidu.com</a>');</script>
2. 防止注入攻击,建议使用 PDO 的参数绑定
使用参数绑定时,PDO 会对各个参数加引号和进行转义
例:
<?php
$user = 'root';
$pwd = '';
try{
$mysql = new PDO('mysql:host=127.0.0.1;port=3306;dbname=test', $user, $pwd);
} catch(Exception $e) {
print 'Database problem:'.$e->getMessage();
die;
}
$st = $mysql->prepare('INSERT INTO family (id,name,is_naive) VALUES (?,?,?)');
$st->execute(array('','Lee',0));
如果不使用参数绑定,则需要自行转义:手动加引号,并且将 SQL 的通配符 _ 和 % 也进行 \ 转义
<?php
$user = 'root';
$pwd = '';
try{
$mysql = new PDO('mysql:host=127.0.0.1;port=3306;dbname=test', $user, $pwd);
} catch(Exception $e) {
print 'Database problem:'.$e->getMessage();
die;
}
$str = "dee's_ books%";
/*使用PDO::quote()自行转义*/
echo $str,"<br />";
// dee's_ books%
echo $safe = $mysql->quote($str),"<br />";
// 'dee\'s_ books%'
$safe = strtr($safe, array('_'=>'\_', '%'=>'\%'));
echo $safe;
// 'dee\'s\_ books\%'
$st = $mysql->query("SELECT * FROM files WHERE contents LIKE $safe");
注意:不论是自定义转义还是使用 PDO::quote() 转义之前,都要判断服务器是否开启了魔法引号(magic_quotes_gpc 在 PHP 5.4.0 中被移除),如果开启了 magic_quotes_gpc,则关闭或者使用 addlashes() 处理传入的参数(尽量关闭魔法引号):
/* magic_quotes_sybase 为 0 时,addlashes() 对 ' " \ 进行 \ 转义 */
/* magic_quotes_sybase 为 1 时,addlashes() 对 ' 进行 " 转义 */
if(get_magic_quotes_gpc() && ! ini_get('magic_quotes_sybase')) {
$str = stripslashes($str);
}
$st = $mysql->prepare('UPDATE files SET contents = ? WHERE id = 1');
$st->execute(array($str));