1.不要相信任何用户输入或第三方数据来源,包括$_GET、$_POST($_FILES)、$_COOKIE、$_SERVER的部分参数等。php
2.HTML、PHP、MYSQL等使用统一的UTF-8编码。html
3.数据库SQL构造时尽可能使用'包裹参数。mysql
4.参数对比时正确使用 === 和 == 。web
5.尽可能选择白名单而非黑名单式过滤。sql
SQL注入的产生在于外界的输入改变了本来定义的SQL语意,譬如:数据库
本来定义的SQL语意,浏览器
$_GET['name'] = 'abc';安全
$name = $_GET['name'];服务器
SELECT * FROM `admin` where user_name = '$name';cookie
最终生成的SQL为,SELECT * FROM `admin` where user_name = 'abc';
外界输入改变后的语意,
$_GET['name'] = 'abc\' or \'a\'=\'a';
$name = $_GET['name'];
SELECT * FROM `admin` where user_name = '$name';
最终生成的SQL为,SELECT * FROM `admin` where user_name = 'abc' or 'a'='a';
此时SQL的语意多加了原定以外的 or 'a'='a' ,因而注入产生。
(1)使用PDO或mysqli预编译处理SQL
使用预处理语句时,PHP请求MySQL将SQL进行预编译,而后再发送参数,所以不管参数是何内容MySQL均将参数看成普通的字符串(或整型、浮点型)处理。
例如上一例子最终生成的SQL为,SELECT * FROM `admin` where user_name = 'abc\' or \'a\'=\'a';
此时没法发生SQL语意的改变,故能防止SQL注入。
注意:PDO兼容大部分数据库,但须要PHP版本5.0+;mysqli只支持MySQL数据库而且须要MySQL数据库版本4.1.13+(服务端版本5.0.7+)。
(2)没法使用PDO或mysqli的预编译处理SQL时,使用addslashes而非mysql_real_escape_string做为临时安全过滤函数,对于参数类型为整型时使用intval将变量转换成整型。
使用addslashes能有效做为安全过滤的前提条件是PHP请求数据库时的字符集为UTF-8且参数位于 '' 内,其余的诸如GBK等因其字符集的特殊性,部分特殊字符转义后仍能造成 ' 从而绕过转义。譬如:
在GBK字符集的环境下,0xbf27通过addslashes转义后获得0xbf5c27,数据库将0xbf5c看成单字节字符而剩余的27则被看成 ' 解析,故而依旧能形成SQL注入。
注 意:在旧版的PHP中(具体版本未知)mysql_real_escape_string是MySQL扩展中的函数,并不是PHP原有函数,此函数在调用时 先判断是否已经链接上数据库,这就意味着mysql_real_escape_string必须是链接数据库以后才能使用,新版的PHP虽然调用时再也不需 要判断是否已经链接上数据库,但会抛出一个警告(mysql_real_escape_string(): The mysql extension is deprecated and will be removed in the future: use mysqli or PDO instead)。
正如《白帽子讲web安全》中所说:XSS本质仍是一直“HTML注入”,用户的数据被当成了HTML代码的一部分来执行,从而混淆了本来语意,譬如:
原语意,
$_GET['page'] = 1;
$page = $_GET['page'];
<a href="http://www.xxx.com/?page=$page">xss</a>
最终生成 <a href="http://www.xxx.com/?page=1">xss</a>
外界输入改变后的语意,
$_GET['page'] = '1" onlick="alert(1)">';
$page = $_GET['page'];
<a href="http://www.xxx.com/?page=$page">xss</a>
最终生成 <a href="http://www.xxx.com/?page=1" onclick="alert(1)">xss</a>
(1)针对普通HTML的输出,使用htmlspecialchars进行安全过滤。
(2)针对连接类型(如图片、超链等)的输出也可以使用htmlspecialchars进行安全过滤,但若是变量是整个URL则应检查这个变量是否以http或https开头,若是不是则自动补齐避免出现伪协议类的XSS攻击。(例子:<a href="data:text/html;base64,PHNjcmlwdD5hbGVydCgxKTs8L3NjcmlwdD4=">xss</a>)
注意:htmlentities
只有使用ASCII或LATIN-1等HTML字符编码时才须要使用,由于htmlentities
会将不少安全的字符也转换成HTML编码。因为htmlspecialchars并不会将 ' 转换成HTML编码,因此只有标签属性是以 "" 包裹变量而非 '' 时使用htmlspecialchars才是安全的。
(3)针对富文本,采用白名单模式放行安全的标签及属性,再针对可能产生XSS的标签和属性作针对的安全过滤。
攻击源以用户的身份伪造请求,譬如:
a.com 站下存在着这样的一个HTML标签,<img src="http://b.com?action=del&id=1">
用户请求a.com时将会产生一条请求 http://b.com?action=del&id=1 (假设这个请求的做用是删除id为1的一篇文章),浏览器会以用户当前浏览器状态在b.com的身份(主要为cookie信息)请求http://b.com?action=del&id=1执行删除id为1的文章操做,这个操做并非用户主观操做而是攻击源以用户身份伪造的操做,而且这个请求是可以被攻击源预知的。
(1)Anti CSRF token,对于敏感的操做(譬如增删改以及敏感信息的查看)使用随机的token进行验证。
注意:Token必须足够随机,且Token验证后应当即删除。Token应尽可能放在表单中采用POST的方式提交避免Token泄露。
正常状况下上传文件是一个合理的功能,漏洞的产生在于用户上传了一个逻辑上并不容许上传的文件类型,这个文件可以被Web容器解析而且用户可以经过Web访问。譬如:
$allowExt = array('image/jpeg','image/x-png','image/gif');
if(in_array($_FILES['file']['type'],$allowExt)){
move_uploaded_file($_FILES['file']['tmp_name'],$_FILES['file']['name'])
}
(1)容许上传的文件类型应采用白名单方式,结合MIME Type和后缀检查文件类型,上传文件中$_FILES['file']['type']所示类型不可信。如上例,
$_FILES['file']['type']为客户端浏览器检测所选上传文件后提供,可是此参数可被人为抓包修改,所以参数不可信。
(2)使用随机文件名保存上传的文件,避免因终止符形成的文件名中断。如上例,
当$_FILES['file']['name']为xxx.php[\0].jpg时,对于低版本PHP(详细版本未知)而言,PHP保存文件的过程当中遇到终止符[\0](0x00的16进制)时误认为文件名已结束,故最终保存在服务器的文件名将是xxx.php。
危险函数执行了咱们原定计划以外的代码,而计划以外的代码可由用户控制,譬如:
$_GET['test'] = 'phpinfo()'';
$test = $_GET['test'];
eval($test);
最终代码等价:phpinfo();
(1)可以执行PHP代码的函数均有可能成为危险函数,危险函数的输入如为用户可控变量时使用白名单模式限制。
(2)使用 preg_replace_callback代替preg_replace的/e模式而且严格控制正则匹配的内容是否可能产生不安全变量。
注意:eval是zend提供的函数而并不是PHP的原生函数,所以经过php.ini disable_functions没法禁用(需经过第三方插件禁用)。
攻击源经过构造请求使目标服务器相信当前请求来源安全并正确响应请求。譬如:
一个修改原始密码的连接,
http://www.abc.com/index.php?action=alterPassword&uid=123456&token=123
目标服务器仅验证token是否有效,有效则容许修改uid为123456的用户密码
(1)对请求的全部参数进行签名防止参数被篡改,而且请求参数必须带有当前请求的时间戳用于目标服务器判断当前的请求是否过时。
源地址:https://www.mudoom.com/Article/show/classify_id/1/id/33.html By佐柱
转载请注明出处,也欢迎偶尔逛逛个人小站,谢谢 :)