接着上回的话题,关于一个系统管理员能够给一台跑着多个应用的服务器做些什么来提高整体安全性。除了限制一些危险函数以外,很重要的便是尽可能地隔离不同的应用,以免一个瘫痪/被攻影响所有。其实,限制危险函数也是为了更好地实现应用隔离——接下来我们会看到,对于某些应用隔离方法,限制某些函数的调用是必须的。这里,我会大致按照隔离的效果降序给出几种常用方法。需要注意到的是,一般来说隔离效果越好,对性能的损失和其他副作用也越大。
禁用部分 PHP 函数加强系统安全
绝大多数 PHP 应用都只是单纯的 WEB 程序,很少用得到一些系统调用。一般最多用到一些列目录、读写删文件等。其他 system 、 passthru 等函数使用的最多的地方是 Web Shell 。作为一个运行着众多应用的服务器的运维,没有办法保证每一个程序的绝对安全,所能做的就是在自己的能力范围内尽可能增强系统安全性,通过权限隔离、限制等方法构筑“最后一道防线”。禁用一些一般不会用到的 PHP 函数就很有帮助。我的做法是在全局的 php.ini 中禁用掉这些函数,对于极个别需要其中某些函数的应用,在相应的 Apache VirtualHost/Directory 配置中通过 php_admin_value 来去掉限制。
禁用版本检查以加快 phpMyAdmin 的页面载入
phpMyAdmin 在更新到 4.x 之后就开始出现奇怪的访问缓慢问题,在 localhost 上遇到过(使用到是 Archlinux 打的包),在服务器上也遇到过(使用的是自己从官网下的压缩包)。有时出现有时又不出现。之前一直觉得是 phpMyAdmin 中 row count 之类功能的设置问题,但调了很久也没解决。主要是问题飘忽不定,急着用的时候碰到了也没空去查,想查的时候又不出现。昨天猛然想到开了个 Firebug 看网络请求,结果一下就发现了问题所在——是被 version_check.php 这个页面请求卡住了……这下才恍然大悟,大概是 4.x 中加入了版本检查功能,可是服务器不一定能够连接外网(比如本机断网或者在内网服务器上),所以就必须等到连接超时……
解决方法很简单,在 config.inc.php
中加入这一行:
$cfg['VersionCheck'] = false;
正确处理下载文件时HTTP头的编码问题(Content-Disposition)
最近在做项目时遇到了一个 case :需要实现一个强制下载功能(即强制弹出下载对话框,阻止浏览器尝试解析显示某些文件格式),并且文件名必须保持和用户之前上传时相同(可能包含非 ASCII 字符)。
前一个需求很容易实现:使用 HTTP Header 的 Content-Disposition: attachment
即可,还可以配合 Content-Type: application/octet-stream
来确保万无一失。而后一个需求就比较蛋疼了,牵扯到 Header 的编码问题(文件名是作为 filename 参数放在 Content-Disposition
里面的)。众所周知, HTTP Header 中的 Content-Type
可以指定内容(body)的编码,可 Header 本身的编码又该如何制定?甚至, Header 究竟是否允许非 ASCII 编码呢?
如果放任编码问题不管,那么你一定会遇到在某个系统及浏览器下下载文件时文件名乱码的情况;如果你尝试搜索解决,那么你很可能会找到一堆自相矛盾的解决方案(我可以负责任地告诉你,其中的99%都是不符合标准的 trick 罢了)。让我们来看看到底应该如何优雅完美地解决这个问题吧!