《PHP与MySQL Web开发》读书笔记——PHP 中代码的安全性

书中讨论了很多Web应用安全性的问题,因为其涉及的面很大很多,本文主要是对其中代码安全性做一个总结。

代码的安全性的指导原则就是检查所有单个组件并且寻找如何改进其安全性的方法。

1.过滤用户的输入

  必须多虑所有来自外部的输入,包括单选、复选、select等,因为即使这些输入控件不像文本框那样可以让用户输入任意字符,但并不能妨碍某些用户直接连接到我们的Web服务器并且在表单中发送任意值。所以,针对用户的输入,更好的策略是验证提交值是否是期望值或允许值。比如性别Select有Male、Female、Other三个值的话,那么在脚本中就该这样去写:

switch ($_POST['gender']) {
    case 'Male':
    case 'Female':
    case 'Other':        

        echo "<p align=\"center\">The user's gender is: " . $_POST['gender'] . "</p>";
        break;
    
    default:
        echo "<p align=\"center\"><span color:red;\">Warning: </span> Invalid input value for gender specified. </p>";
        break;
}

相同的道理,某些我们期望是数值的表单域就可以通过简单的类型转换来验证其有效性,如下代码所示:

$number_of_nights = (int)$_POST['num_nights'];
if ($number_of_nights == 0) {
    echo "ERROR: Invalid number of nights for the room!";
    exit;
}

因此,作为一条法则,我们不能假设来自表单的值就会是期望值——必须首先检查提交值。

当用户的输入需要存入到MySQL数据库时,则更需要非常慎重的对待,要了解一些常用的防止SQL注入的方法。书中提供了两种方法,一是过滤并转义所有发送给数据库的字符串,使用mysql_real_escape_string函数等;二是确认所有输入都符合期望值。由于SQL注入是一个较大的话题,详细的可以参考PHP的文档。

2. 转义输出

在系统保存用户输入后,确保这些值不会被破坏或者导致任何无意结果是非常重要的。通过几个关键函数,即可以保证这些值不会被客户端Web浏览器错误的执行,而只是显示文本,这两个函数是 htmlspecialchars 和 htmlentities。这些函数将检查用户的输入,并将特定的字符转化为HTML实体。两个函数的差别及用法可以参考PHP的文档。

3.代码组织

简单的说,互联网上任何不能被用户直接访问的文件都不应该保存在Web站点的文档根目录。例如文档根目录为/home/httpd/messageboard/www, 应该将所有引入文件以及为站点编写的其他文件保存在其他位置,如/home/httpd/messageboard/code。那么在引入时,可以使用require_once('../code/user_object.php')等。

这样做的原因是当一个恶意用户请求一个非php或html文件时可能发生的状况,默认情况下Web服务器会将文件的内容导出到输出流。要更好的避免这种情况,还必须确保配置Web服务器为只允许请求php和html文件,而对其他类型的文件请求必须返回错误。

同样的,任何其他文件,如密码文件、配置文件等,都必须与公众文档根目录隔离。

还有,不要启用php的allow_url_fopen选项,避免引入其他机器上的文件。

4. 代码自身的问题

  如数据库的连接

$conn = @new mysqli("localhost", "bob", "secret", "somedb");

  尽管很方便,但是很不安全,如果破解人员能够看到这个PHP的源文件,则立马就能获得一个数据库的账户和密码。所以最好不要将保存用户名称和密码的文件保存在Web应用的文档根目录,而是在脚本中再引入文件,如下所示:

//this is dbconnect.php
$db_server = "localhost";
$db_user_name = "bob";
$db_password = "secret";
$db_name = "somedb";

  然后在需要时引入该文件即可。

  即相当于构建一个安全保护的中间层。

5.文件系统因素

  PHP是能够与本地文件系统进行交互的。必须确保在PHP与文件系统交互时的权限问题,保证用户不能看到私密的文件,如php.ini等。

6.代码的稳定性和缺陷

  在应用开始阶段计划其稳定性,可以有效降低由于人为错误带来的问题的可能性。

  计划步骤包括如下所示:

  1)完成完整的产品设计阶段,可能的话还包括设计原型。

  2)为项目分配测试资源。

  3)开发提交前单元测试。

  4)应用部署后监视运行。

7.执行引号和exec

  如果允许用户输入出现在反引号的命令中,就会面临各种安全问题,必须严格的过滤用户输入以确保用户安全,如至少使用escapeshellcmd函数。而exec则可以直接执行命令,更应该特别注意。这个问题一般应用中很少涉及,书上谈的也很少,在用的时候可以在网上搜索相关的文档。