Web框架
执行环境
一个单一共享文件php.ini
,控制了
PHP的大部分功能并织入了复杂的针对覆盖什么与何时覆盖的规则
。PHP软件能部署在任意的机器上
,因此必须覆盖一些设置使环境正常,这在很大程序上会违背像php.ini这样的机制的使用
。
PHP基本上以CGI运行。每次页面被点击,PHP在执行前,重编译整个环境。就连Python的玩具框架的开发环境都不会这样。
这就导致了整个“PHP加速器”市场的形成,仅仅编译一次,就能加速PHP,就像其它的语言一样。Zend,PHP的幕后公司,將这个做为它们的商业模式。
很长时间以来,PHP的错误默认输出给客户端—我猜是为开发环境提供帮助。我不认为这是真相,但我仍然看到偶尔会有
mysql错误出现在页面的顶部。
在<?php…?>标签外的空白,甚至在库中,PHP以文本对待并解析给响应(或者导致“headersalreadysent”错误)。一个流行的做法是忽略?>关闭标签。
部署
部署方式常常被引述为PHP的最高级部分:直接部署文件就可以了。是的,这比需要启动整个进程的Python或Rury或Perl要容易。但PHP留下了许多待改进的地方。
我很乐意以应用
服务器的方式运行Web应用程序并反向代理它们。这样的代价最小,而好处多多:你可以单独管理
服务器和应用程序,你可以按机器的多或少运行运行多个或少量应用进程,而不需要多个web服务器,你可以用不同的用户运行应用,你可以选择web服务器,你可以拆下应用而无需惊动web服务器,你可以无缝部署应用等等。將应用与web服务器直接焊接是荒谬的,没有什么好的理由支持你这么做。
每个PHP应用程序都使用php.ini。但只有一个php.ini文件,它是全局的;如果你在一个共享的服务器上,需要修改它,或者如果你运行两个应用需要不同的设置,你就不走运了;你不得不向组织申请所有必须的设置并放在应用程序,如使用ini_set或在Apache的配置文件或在.htaccess设置。如果你能做的话。可能wow,你有大量的地方需要检查以找出怎样获取已设置的值。
类似的,“隔离”PHP应用的方法也不容易,它依赖于系统的其它部分。想运行两个应用程序,想要不同的库版本,或不同的PHP版本本身?开始构建另一人Apache的拷贝吧。
”一堆文件”方案,除了使路由像只病重的笨驴外,还意味着你不得不小心处理白名单或黑名单,以控制什么东西可访问,这是因为你的URL层次也就是你的代码树的层次。配置文件和其它的”局部模块”需要C之类的东西守护以避免直接加载。版本控制系统的文件(如.svn)需要保护。使用mod_php,使得文件系统的所有东西都是潜在的入口;使用应用服务器,仅有一个入口,并且仅通过URL控制调用与否。
你不能无缝的升级那堆以CGI-style运行的文件,除非你想要应用崩溃和出现未定义行为,当用户在升级的间歇期点击你的站点时。
尽管配置Apache运行PHP很”简单”,仍然会有一些陷阱。而PHP文档建议使用SetHandler使得.php文件以PHP方式运行,AddHandler看起来运行良好,然而事实上会有问题。
当你使用AddHandler,你在告知Apache“以php执行它”,这是一个可能的处理.php文件的方式。但!Apache对文件的扩展名不这样认为。它被设计为能支持如,index.html.en这样的文件。对于Apache,文件可以同时具有任意数量的扩展名。
猜想,你有个文件上传的表单,存储一些文件到公共目录中。确保没人能上传PHP文件,你仅仅检查文件不能有.php扩展名。所有的攻击需要做的只是上传以foo.php.txt命名的文件;你的上传工具不会看出问题,Apache会认为它是个PHP,它会很高兴的执行。
这里不是“使用原始文件名”或“没有更好的验证”导致的问题;问题是你的web服务器要被配置用来运行任何旧代码,使得PHP“容易部署”。这不是理论上的问题;我已发现很多实际的站点有类似的问题了。
缺失的特性
我认为所有这些都是以构建一个Web应用为中心的。对PHP看起来很合理,是它的销售卖点之一,它是“Web语言”,理应有它们。
无模块系统。PHP就是
模版。
无XSS过滤器。htmlspecialchars不是XSS过滤器。
无CSRF保护。你必须自己做。
无通用标准的数据库API.像PDO这类东西不得不包装每个特定数据库的API,分别抽象不同部分。
无路由系统。你的站点结构就是你的文件系统结构。
无认证或授权。
无开发服务器。
无交互调试模式。
无一致的部署机制;仅仅”拷贝所有文件到服务器中”。
安全
语言边界
PHP的蹩脚
安全机制可能会放大,因为它利用某语言拿出数据,又把它转存到另一个中。这是个坏注意。“<script>”可能在
SQL中意味着什么都不是,但在HTML中就很是了。
让情况更糟糕的是通常有人哇哇喊到“你的输入要消毒”。那完全错误;你不可能有什么魔法使块数据完全”干静”。你需要做的就是对语言说:
SQL使用占位符,进程孵化使用参数列表,等等。
PHP公然鼓励“消毒”:有个数据过滤扩展可以做到。
所有的addslashes,scripslashes,和其它的slashes相关的东西都是废物,毫无用处。
我只能告诉你这么多,无法安全的孵化进程。你仅能通过shell执行字符串。你的选择是疯狂的转义,并希望默认的shell使用正确的转义,或手动的pcntl_fork_exec和pcntl_exec.
所有的转义命令和转义参数存在大致相同的描述。注意在Windows中,转义参数不工作(因为它假设成Bourneshell语议),转义命令仅仅用空格替换一堆标点符号,因为没人能搞清楚Windows命令转义行为(它可能默默的破坏你试图做的任何事情)。
原始的内建MySQL绑定,仍然广泛使用,它无法创建preparedstatements.
直到今天,PHP文档关于SQL注入的建议还是让人抓狂的做如类型检查,使用sprintf和is_numeric,在每个地方手动的使用
mysql_real_escape_string,或在每处手动使用addslashes(这个”可能更有用”!)这样的实践。并没有提到PDO或参数化,除了在用户评论中有点线索。至少在两年以前,我就有具体的向PHPdev抱怨过了,他被惊动了,而页面却从未变过。
Insecure-by-default
register_globals.它被默认关闭的,而在5.4中去除了。我不在乎。
include接受HTTLURLS.和上面一样。
Magicquotes.Soclosetosecure-by-default,andyetsofarfromunderstandingtheconceptatall.
核心
PHP解释器本身就有一些恼人的安全问题。
2007年的时候,解析器有个整数溢出
漏洞。修复始于if(size>INT_MAX)returnNULL;从那以后就走下坡路了。(对于那些不需要使用C的人:曾经,INT_MAX是适合变量最大整数。我希望你能从这里搞清楚其余的东西。)
最近,PHP5.3.7包括了个crypt()函数,有个
漏洞让任何人可以用任何密码登录。
PHP5.4是容易遭受拒绝服务攻击,因为它需要Content-Length头(任何人都可以设置),并试图分配更多内存。这是一个坏主意。
我可以挖掘更多,但重点不是这有很多X漏洞—是软件就有bugs,无论如何都有。这些自然是令人咋舌。我并没有特意寻找这些;但在过去的几个月里,它们自己送上门来了。
总结
一些评论会理所当然的指出我没得出任何结论。好吧,我是没有结论。如果你一路看到了这里,我假设一开始你就同意我了。
如果你仅了解PHP而对学习其它东西感兴趣,可以看看Python教程,尝试Flask这个为web准备的家伙。(我不是它的
模版语言的铁杆粉丝,但它确实很好的完成了这些工作。)它將你的应用分成多个部分,但它们看起来仍然是一致的。我可能稍后会写个关于这个的贴子;旋风般的介绍整个语言和不同于这里所说的web堆栈。
之后或对于更大的项目,你可能需要Pyramid,一个中等规模的框架,或者是Django,一个构建站点的复杂的框架,如Django站点。