九/087
PHP Architect’s Guide to PHP Security [1]
这几天稍微轻松点,就再看了一次PHP Architect’s Guide to PHP Security这本经典PHP安全书籍。其中整理了一些笔记之类的东西,分享给大家。都是个人组织和翻译的东西,如果有不周到的地方还请广大安全爱好者给出批评和指正。本来想逐字翻译全文,但是这是一个浩大的工程,并且学习意义不大,所以挑选一些重要的内容进行翻译和注释。


前言和介绍就略过不翻译了,作者描述了一下PHP安全的重要性,以及为何变得如此重要。我想目前线上的很多php站点遭到攻击和破坏足以说明这点了,我们直接来看第一章Input Validation。今天先写前半章吧。(一章内容太多,本人英文比较菜,看完需要差不多3个小时)
该次翻译的内容列表:
- The Trouble with Input. (输入的问题)
- The Constant Solution (常量解决方案)
- The $_QEGUEST Trojan Horse ($_REQUEST木马)
- Validating Input (输入验证)
- Validating Numeric Data (数字验证)
- Locale Troubles (Locale问题)
- String Validation (字符串验证)
- Content Size Validation (内容长度验证)
这章简介中作者用斜体标注出了这么一句话:It is absolutely imperative to validate all user input to ensure it matches the expected form.
为了确保数据是我们期望的,必须验证所有的用户输入,即那句话:“永远不要信任外部数据”。
紧接着作者讲解了magic quotes gpc出现的历史,跟早起的版本相比较,是简化了开发者的工作,也增加了安全性。因为对外部来的数据不再对来源污染了,我们可以很清楚的知道数据的来源。这里作者给出一个例子:
if (is_authorized_user())
define(‘auth’, TRUE);
if (auth) // will always be true, either Boolean(TRUE) or String(“auth”)
/* display content intended only for authorized users */
为了“安全”,该程序将auth这个值设置为常量,避免数据污染和没有初始化带来的数据覆盖。但是,PHP中对于一个字符串,如果没有用引号包含起来,会当作常量处理,如果没有这个常量,将作为字符串处理。所以这里的auth放在if语句中就算没有define,也是成立的,因为他是一个字符串。这时只会给出一个notice的错误,不影响程序的执行,并且一般上线的站点,都会把display_errors关掉,根本不会提示。这里作者顺带提及了一个变量覆盖的问题,gpc_order指令可以设置优先级。并给出一个原则:尽量对所有变量进行初始化。
接下来,Validating Numeric Data这个小节说明对数字进行验证和处理最好的办法就是用int或float进行强制转换,比如:
$_GET[‘product_id’] = (int) $_GET[‘product_id’];
$_GET[‘price’] = (float) $_GET[‘price’];
这里注意一点,如果我们用is_numeric函数对8进制数字进行判断时,不一定返回的就是我们需要的结果。is_numeric(“0955”);返回的是true,但显然这不是一个有效的8进制数值。
接着作者说明了local对验证的影响,这里不再详述,只要使用setlocale语法就可以了。
关于字符串的验证,一般使用的是ctype类函数,不过这些函数局限性也很大,比如认为-,空格等字符是非法的,再加上ctype是一个独立的扩展,有可能服务器上并没有安装该扩展或者被屏蔽,而且只能用在单字节字符集,所以最好的替代方案就是使用正则表达式(比如ereg函数)。Alas, regular expressions aren’t exceptionally fast and validating large strings of data may take noticeable amount of time. But, safety must come first. 正则表达式不是特别快,当对比较长的字符串数据进行验证的时候,会占用很明显不少的时间。但是,安全第一。
这篇文章最后,说下内容长度验证的问题:
表单中,只有text和password类型的控件可以使用maxlength属性设置最大长度,如果我们想在textarea中限制长度,可以使用下面这小段js做到。
<form onSubmit=”if (this.biography.value.length > 255) {
alert(‘Keep it short, eh?’)
return false;
}”>
<textarea name=”biography”></textarea><input type=”submit”>
</form>
提交表单时候,用javascript判断这个textarea的长度是否大于255。不过任何form中的限制和js做的限制都不是一劳永逸的,If JavaScript and HTML can be circumvented, server-side PHP provides the real stopgap.
$form_fields = array(“Fname”=>50, “Lname”=>100, “Address”=>255, /* . . . */);
foreach ($form_fields as $k => $v)
if (!empty($_POST[$k]) && strlen($_POST[$k]) > $v)
exit(“{$k} is longer then the allowed {$v} byte length.”);
必须用服务器端脚本进行验证,比如上述php代码则可以完成。
此外,如果你是一个对效率要求很苛刻的人,strlen会觉得影响速度,没关系,可以试试下面这个方法:
$form_fields = array(“Fname”=>50, “Lname”=>100, “Address”=>255, /* . . . */);
foreach ($form_fields as $k => $v) {
if (!empty($_POST[$k]) && isset($_POST[$k]{$v + 1})) {
exit(“{$k} is longer then the allowed {$v} byte length.”);
}
}
在PHP4.3.10后,可以对字符串使用isset控制,Because isset() is a language construct, it’s converted to a single instruction by Zend’s PHP parser and takes virtually no time to execute. 因为isset是一个语言结构,这样会加快执行的速度。似乎有点吹毛求疵,不过这正是我们狼族小四此等人的目的:-)
今天先写到这里吧,可能翻译整理得比较笼统,剩下的东西大都是解释型的句子,我想大部分人都很了解了吧。
12:15 on 九月 26th, 2008
我先扔个板砖啊!
12:31 on 九月 26th, 2008
真厉害 我英语更惭愧了
12:46 on 九月 26th, 2008
呵呵,欢迎拍板砖。
我英语也菜,凭着兴趣坚持。
12:53 on 九月 26th, 2008
我扔!!!!!!!!!!相当不错!!!!
14:06 on 九月 26th, 2008
为了顶你我把广告全点了次……
14:32 on 九月 26th, 2008
秋香妹妹,像你这么好的同志现在不多了。
18:04 on 九月 26th, 2008
楼上的小弟,你站稳了,我过去抽你。