导读:这是我阅读Essential PHP Security的整理笔记。希望通过实际的笔记型阅读,更深刻的理解安全这一概念。内容将根据书目进行。
详细:
1 简介
1.1 全局变量注册
使用超级公用数组$_GET $_POST代替。尽量初始化变量——如果明确能获取预期值,可以不用。
1.2 错误报告
在生产环境需要关闭所有的错误报告。这可以从php.ini中设置。等同于php代码中使用:
ini_set('error_reporting', E_ALL | E_STRICT); ini_set('display_errors', 0);
2 四大原则
2.1 深度防范
安全措施冗余。时刻备份方案。合理进行措施冗余和备份方案,不可过渡,这样会造成成本的增加和降低价值。
2.2 最小权限
风险最小化。应该考虑什么权限是必须的,职能赋予每个人完成本职工作的所必须的尽量少的权限。无需考虑一项权限被滥用的所有方式,因为这是不可能完成的工作。
2.3 简单就是美
复杂滋生错误,错误导致安全漏洞。这个简单的事实说明了为什么简单对于一个安全的应用来说是多么的重要。没必要的复杂与没有必要的风险一样糟糕。看如下的代码:
# code 1 $search = isset($_GET['key_word') ? $_GET['key_word') : ''; # code 2 $search = $_GET['key_word'];
这里提出一个概念变量受污染:即在程序的执行过程中,改变量的值不是由赋值语句直接制定的,而是来自其他来源,如控制台输入,数据库等。
code1 中,规避了$search 被污染的事实,我们在后续的程序中,无法判定他是否被污染。而使用code2 将可以对获取的原始值,查看它是否被污染并进行安全校验。
2.4 暴露最小化
PHP应用程序需要与外部数据进行通信,主要源是客户端浏览器和数据库。时刻小心防止数据被暴露在Internet上。
数据暴露不一定就意味着安全风险,但是,数据暴露必须尽量做到最小化。使用SSL。
为了降低对敏感数据的暴露率,必须确认什么数据是敏感的,同时跟踪它,并消除所有不必要的数据暴露。
3 方法
3.1 平衡风险与可用性
用户操作的友好性与安全措施是一对矛盾,在提高安全性的同时,通常会降低可用性。在为非预期流程书写代码时,必须要考虑符合逻辑的正常流程。并努力寻找到这一平衡点。
尽量使安全措施对用户透明,让他们感受不到它的存在。如果实在不可能,就尽量采用用户比较常见和熟悉的方式来进行。如用户认证。
当你怀疑可能的非法操作时,必须意识到可能会搞错了。如,疑问用户身份,使用在此输入登录密码,而不是从技术上来说等同的再次登陆系统,因为这给用户带来的体验是有很大的区别的。没有必要将用户踢出系统并显式的指责他们是所谓的攻击者。
对疑是攻击行为采取小心和明智的反应。
3.2 数据跟踪
对于一个有安全意识的开发者,最重要的一件事就是随时跟踪数据。不仅仅是知道它是什么和它在哪里,还要知道它来自哪里,要去到哪里——数据流。这需要对web的运作原理有深刻的理解。
需要做到如何区分可信和不可信的数据。
PHP通过超级全局数组$_GET\$_POST\$_COOKIE呢g难清楚的展示出用户数据的来源。一个严格的命名体系能保证在程序代码的任何部分知道所有数据的来源。
在做代码审查(Code Review)排除安全漏洞时,需要仔细的检查代码中与外部系统的交互部分。
3.3 过滤输入
过滤时WEB应用安全的基础中的基础。就是验证数据合法性的过程。通过在输入时确认对所有数据都进行了过滤,可以避免被污染的数据在程序中被误信和误用。
过滤有三个步骤:-识别输入 -过滤输入 -区分已过滤及被污染数据。
把超级公用数组整个当作用户输入,因为这些数组中的某些元素很难判定,如$_SERVER。
把session的保存位置和数据库也看作输入更为安全。
过滤数据时,不要试图去纠正非法数据,而是要让用户按照你确定的规则进行输入,因为纠正的行为会导致不可预知的安全漏洞。
使用$clean 来保存已经过滤的数组。这时对于过滤一组已知的合法值的数据很有效,但对于过滤一组已知合法字符组成的数据时就没有什么帮助了。如:
$clean = array(); // 初始化为空数组以防止包含被污染的数据 if(ctype_alnum($_POST['username'])){ $clean['username'] = $_POST['username']; }
说明,在这种情况下,尽量使用PHP内置函数。
3.4 输出转义
对输出进行转义或对特殊字符进行编码,以保证原意不变。
转义有三个步骤:-识别转义 -输出转义 -区分已转义与未转义数据。
A 往客户端浏览器输出:
$html = array(); // 存储已过滤和已转义数据 $html['username'] = htmlentities($clean['username'], ENT_QUOTES, 'UTF-8'); echo "<p>Welcome back, {$html['username']}.</p>"; // 使用htmlentities 替代htmlspecialchars
B 往数据库输出:
$mysql = array(); $mysql['username'] = mysql_real_escape_string($clean['username']); $sql = "SELECT * FROM profile WHERE username = '{$mysql['username']}'"; $result = mysql_query($sql);
说明,这里建议使用mysqli及PDO对数据库进行操作。