PHP安全编程
转自:http://www.nowamagic.net/librarys/veda/detail/2076
1.关闭register_globals,以提高安全性
2.在部署环境,不要让不相关的人看到报错信息,可以如此设置:
ini_set('error_reporting', E_ALL | E_STRICT); ini_set('display_errors', 'Off'); ini_set('log_errors', 'On'); ini_set('error_log', '/usr/local/apache/logs/error_log');
或者设置错误处理函数:
set_error_handler('my_error_handler');
function my_error_handler($number, $string, $file, $line, $context) { $error = "= == == == ==\nPHP ERROR\n= == == == ==\n"; $error .= "Number: [$number]\n"; $error .= "String: [$string]\n"; $error .= "File: [$file]\n"; $error .= "Line: [$line]\n"; $error .= "Context:\n" . print_r($context, TRUE) . "\n\n"; error_log($error, 3, '/usr/local/apache/logs/error_log'); }
3.深度防范/最小权限/简单就是美/暴露最小化原则
4.平衡风险与可用性/跟踪数据
5.过滤用户输入
1)识别输入:_GET/_POST/_SERVER,session,数据库
2)过滤输入:防止非法数据进入你的应用,并且只做检查,不做纠正,提高安全性
3)区分已过滤及被污染数据:利用一个$clean数组来保存过滤后的数据,但是需要做两件事情:
a.经常初始化该数组为一个空数组
b.加入检查及阻止来自外部数据源的变量命名为clean
6.对输出要转义
1)识别输出:echo/print/printf/<?=
2)过滤输出:htmlentities/htmlspecialchars/mysql_real_escape_string/addslashes
3)区分已转义及未转义数据:利用一个$html数组来保存转义后的数据
7.利用session相关机制来避免URL语义攻击
8.防御文件上传攻击
1)限制上传大小:upload_max_filesize/post_max_size
2)对缓存区的数据进行确认:is_uploaded_file/move_uploaded_file/filesize
9.利用"过滤输入,转义输出"的原则来降低XSS攻击的风险
10.利用POST和Token来降低CSRF的风险
特别需要指出的是,习惯上GET与HEAD方式不应该用于引发一个操作,而只是用于获取信息。这些方式应该被认为是‘安全’的。客户浏览器应以特殊的方式,如POST,PUT或DELETE方式来使用户意识到正在请求进行的操作可能是不安全的
11.不要使用HTTP的头中的Referer,而是采用过滤输入的方式来避免欺骗表单的提交
12.还是采用过滤输入的方式来避免欺骗HTTP的接受
13.不要将配置文件放在根目录,也不要采用其他的特殊后缀名,防止暴露配置信息
14.利用"过滤输入,转义输出"的原则来降低SQL注入风险
1)利用参数化查询语句
2)配置magic_quotes_gpc自动对GET和POST数据进行addslashes处理
15.通过防止CSRF漏洞和修复暴露cookie的浏览器漏洞相结合来防止cookie盗窃
16.Session回话固定
SessionID是浏览器和后台交易的基础,浏览器可以通过固定或者捕获的方式来获得该数值,有以下几种防御方式:
1)可以在权限等级变化时,利用session_regenerate_id来每次重新生成
2)修改PHPSESSID,改成一些没有规律的名称
3)比较每次请求的$_SERVER['HTTP_USER_AGENT'],不同就有嫌疑
4)采用更为俺安全的SSL,防止信息被泄露
17.源代码的暴露
1)对包含文件使用非常用的扩展名,例如:inc等
2)包含文件保存在网站主目录下
3)Apache未设定.inc文件的类型
4)Apache的默认文件类型是text/plain
18.为了阻止文件名被操控,当然最有效的方法就是过滤输入,此时之外,还有以下几个方法
1)没有路径信息的文件名,用basename过滤
2)允许有路径信息但想要在检测前把它化简,用realpath过滤
3)还可以用pathinfo来获得路径信息
4)关闭allow_url_fopen,不能用include/require访问远程文件
19.通过限制登录次数或登录间隔,来限制暴力破解攻击
20.攻击者可以用抓包软件来嗅探用户密码,最好的方法是采用HTTPS
21.攻击者得到密码后,就可以进行重播攻击,可以采取的防御为
1)避免受保护资源永久访问权的的使用
2)避免受保护资源访问权的临时性
22.如果非要进行永久访问权的使用,可以采用的方法为
在username和password的基础上,加上identifier/token/timeout字段,加如二次验证
1)生成
$salt = 'SHIFLETT'; $identifier = md5($salt . md5($username . $salt)); $token = md5(uniqid(rand(), TRUE)); $timeout = time() + 60 * 60 * 24 * 7; setcookie('auth', "$identifier:$token", $timeout);
2)验证
$clean = array(); $mysql = array(); $now = time(); $salt = 'SHIFLETT'; list($identifier, $token) = explode(':', $_COOKIE['auth']); if (ctype_alnum($identifier) && ctype_alnum($token)) { $clean['identifier'] = $identifier; $clean['token'] = $token; } else { /* ... */ } $mysql['identifier'] = mysql_real_escape_string($clean['identifier']); $sql = "SELECT username, token, timeout FROM users WHERE identifier = '{$mysql['identifier']}'"; if ($result = mysql_query($sql)) { if (mysql_num_rows($result)) { $record = mysql_fetch_assoc($result); if ($clean['token'] != $record['token']) { /* Failed Login (wrong token) */ } elseif ($now > $record['timeout']) { /* Failed Login (timeout) */ } elseif ($clean['identifier'] != md5($salt . md5($record['username'] . $salt))) { /* Failed Login (invalid identifier) */ } else { /* Successful Login */ } } else { /* Failed Login (invalid identifier) */ } } else { /* Error */ }
3)退出
setcookie('auth', 'DELETED!', time());
23.尽量将Session数据保存在Mysql或Memcache中,减少因文件被操控后被猜中后泄露和操控的机会
24.PHP安全模式生效时,PHP会对正在执行的脚本所读取(或所操作)文件的属主进行检查,以保证与该脚本的属主是相同的,他是一种深度防范