php 安全基础 第八章 共享主机 源码暴露
8.1. 源码暴露
你的WEB服务器必须要能够读取你的源确并执行它,这就意味着任意人所写的代码被服务器运行时,它同样可以读取你的源码。在一个共享主机上,最大的风险是由于WEB服务器是共享的,因此其它开发者所写的PHP代码可以读取任意文件。
<?php
header('Content-Type: text/plain');
readfile($_GET['file']);
?>
通过在你的源码所在的主机上运行上面脚本,攻击者可以通过把file的值指定为完整的路径和文件名来使WEB服务器读取并显示任何文件。例如,假定该脚本命名为file.php,位于主机example.org上,只要通过访问下面链接即可使文件/path/to/source.php的内容暴露:
http://example.org/file.php?file=/path/to/source.php
当然,要使这段简单的代码起作用,攻击者必须确切地知道你的源码的位置,但是攻击者可以写出更复杂的脚本,通过它可以方便在浏览整个文件系统。关于此类脚本,请看本章后面部分的示例。
对该问题没有完美的解决方案。正如第五章所述,你必须考虑所有你的源码都是公开的,甚至是保存在WEB主目录之外的代码也是如此。
最好的办法是把所有敏感数据保存在数据库中。虽然这使一些代码的编写多了一层复杂性,但这是防止你的敏感数据暴露的最好方法。很不幸的是,还有一个问题。如何保存你的数据库访问密码?
请看保存在网站主目录之外一个名为db.inc的文件:
<?php
$db_user = 'myuser';
$db_pass = 'mypass';
$db_host = 'localhost';
$db = mysql_connect($db_host, $db_user, $db_pass);
?>
如果该文件的路径是已知的(或被猜中),就存在着你的服务器上的另外一个用户访问该文件的可能,就会获取数据库访问权限,这样你保存在数据库中的所有数据就会暴露。
解决该问题的最好方案是把你的数据库访问权限以下面的格式保存在一个只有系统管理员权限才能读取的文件中:
SetEnv DB_USER "myuser"
SetEnv DB_PASS "mypass"
SetEnv是一个Apache的指令,上面文件的意思是建立两个分别代表你的数据库用户名及密码的Apache环境变量。当然,该技巧的关键是只有系统管理员才能读取该文件。如果你无法登录成为系统管理员,你就可以限制该文件只能由你自已进行读取,这样的保护方式与上面的方式类似。
$ chmod 600 db.conf
$ ls db.conf
-rw------- 1 chris chris 48 May 21 12:34 db.conf
这就有效地防止了恶意脚本访问你的数据中权限,因此对于数据库中保存的敏感数据来说,不会有危及安全的重大风险。
为使该文件生效,你就需要能够通过PHP访问其中的数据。要达到这个目的,需要在httpd.conf中写上如下的包含句:
Include "/path/to/db.conf"
需要注意该语句需要插入在VirtualHost区域内,否则其它用户就能取得相应的内容。
由于Apache的父进程以系统管理员身份运行(需要绑定在80端口),它能够读取该配置文件,但处理服务器请求的子进程(运行PHP脚本)不能读取该文件。
你可以通过$_SERVER超级全局数组去访问这两个变量,这样在db.inc中,只要通过引用$_SERVER变量即可,而不是在其中写明数据库的权限:
<?php
$db_user = $_SERVER['DB_USER'];
$db_pass = $_SERVER['DB_PASS'];
$db_host = 'localhost';
$db = mysql_connect($db_host, $db_user, $db_pass);
?>
如果该文件被暴露,数据库访问权也不会泄露。这对于共享主机是一大安全性改进,同时对于独立主机也是一种深度防范手段。
注意在使用上述技巧时,数据库访问权限就位于$_SERVER超级公用数组中。这就需要同时限制普通访问者运行phpinfo()察看或其它任何导致$_SERVER内容暴露的原因。
当然,你可以使用本技巧保护任何信息(不只是数据库访问权限),但我发现把大多数数据保存在数据库更为方便,特别是由于该技巧需要得到你的主机提供商的协助。