安全|程序
php是一种跨平台的服务器端的嵌入式脚本语言。它大量地借用c,java和perl语言的语法, 并耦合php自己的特性,使web开发者能够快速地写出动态产生页面。最新版本php5.01支持目前绝大多数数据库(mysql、postgresql、oracle,、db2、sybase等)。还有一点,php是完全免费的,不用花钱,你可以从php官方站点(http: //www.php.net)自由下载。php拓展了web server的功能,实现了web最佳服务的后端延展界面,见图-1。
图-1 php拓展了web server的功能
php全称professional hypertext preprocessor。以最新的php5.01为例支持它的web服务器有:apache, microsoft internet information sereve, microsoft personal web server, aol server, netscape enterprise 等等。
php是一种功能强大的语言和解释器,无论是作为模块方式包含到web服务器里安装的还是作为单独的cgi程序程序安装的,都能访问文件、执行命令或者在服务器上打开链接。而这些特性都使得php运行时带来安全问题。
虽然ph p是特意设计成一种比用perl或c语言所编写的cgi程序要安全的语言,但正确使用编译时和运行中的一些配置选项以及恰当的应用编码将会保证其运行的安全性。由于我们可以在很多不同的方面利用 php,因此它有很多设置选项来控制其行为。
一组庞大的可选参数能够保证您可以将 php 用于许多不同的目的,但这同时也意味着这些参数和服务端配置的组合会带来一些安全问题。 php 的配置与其代码相比,有着同样的灵活性。php 可以用来建立完整的服务端应用程序,拥有所有外壳用户的权限;它也可以在被严格控制的环境下用作一个简单的服务端包含,仅承担很小的风险。您如何建立该环境,以及其安全性如何,在很大程度上取决于 php 的开发者。 注:本文所有操作在red hat linux 9.0下完成。
一、 安全从头开始
在编译php之前,首先确保操作系统的版本是最新的,必要的补丁程序必须安装过。安装编译php过程中要注意的4个问题:
1、 使用apachetoolbox整合apache,php,mysql 目前最好的web建站黄金组合是linux+apache+mysql+php, 但是在实际工作过程中需要分别下载,安装,配置apache,php和mysql,并且需要根据具体情况修改apache的httpd.conf, php的php.ini还有mysql的配置文件,如果你还需要提供ssl功能,那还得下载正确的ssl apache模块,并定制它的.ini文件等,其中的任何步骤出现问题都会导致网站不能正确运行。
想一次完全配置成功,即使对于一个经验丰富的linux网络管理员也比较困难。apache toolbox是用shell脚本写成的。apache toolbox可以很很方便的使你定制你的apache按您的要求在apache支持的52个第三方的软件包以及36个模块中选择。
定制的过程完全用菜单驱动,而且都有简单的说明。所有的组件都是用源代码方式安装,在安装过程中,如果发现rpm包有问题,它还会用wget去重新下载新的可用的组件包。相信对那些网络管理员是一个有用的工具。
apache toolbox 可以在gun命令行下安装,也可以在x窗口下安装,为了方便读者阅读本文以在x窗口下安装为例。在www.apachetoolbox.com下载最新的apachetoolbox安装包。包括apache2.0,mysql3.23.51,php4.3,python 2.0、 postgressqlv7.1等常用建站软件和apc (一种为php提供cache的模块),apache toolbox apache toolbox提供了一个简单的编译apache方法,能让你很容易地安装apache、 ssl,php, zendoptimizer, mod_auth-nds,mod-dynvhost,webdav,mod_fastcgi,mod_gzip,mod_layout,mod_throttle,mod_accessref, mod_auth_sys, mod_bandwidth, mod_auth_ldap, mod_perl, openldap.等等以及最新的支持png格式的 gd 库。它支持完全的菜单界面。
图-2 apachetoolbox安装界面 2、按照apache 模块安装当 php 被用作 apache 的模块时,它将继承 apache 的用户权限(典型情况为用户nobody)。 这将对安全及授权机制产生一些冲击。
例如,如果您使用 php 来访问数据库,除非数据库本身有内建的访问控制,否则您将使得数据库能够被用户nobody访问。这意味着恶意的脚本能够访问并修改数据库,甚至不需要用户名和密码。网络黑客无意中访问到了数据库管理员的 web 页面并通过这里删除所有的数据库是完全有可能发生的。
您可以利用 apache 的认证机制来防止这些的发生,或者也可以利用 ldap 或 .htaccess 文件等来设计您自己的访问模式,并将这些代码包含为您 php 脚本的一部分。
通常,一旦安全机制建立并使得 php 的用于(在这种情况下,为 apache 用户)仅为此承担很小的风险时,我们发现 php 此时被禁止往用户目录写入任何文件,或者还有可能被禁止访问和更改数据库。无论往防止的对象中写入文件的好坏以及进入的数据库事务的好坏,其安全性都是同等的。
在这个时候,一个频繁出现的安全错误是给 apache root 权限,或者用其它方法提升 apache 的能力。
给 apache 用户赋予 root 权限是及其危险的,而且有可能会连累整个系统。因此,进行 sudo、chroot,或者以 root 账号运行等操作不应该考虑让那些非安全专家来执行。 还有一些更简单的情况。您可以使用 open_basedir 来控制和限制 php 能够使用的目录。您还可以建立 apache 的专用区域,以将所有基于 web 的活动都限制到非用户、系统和文件。
3、把php解析器放在web目录外
一种安全性非常高的方法是把 php 解析程序放置到 web 文件目录树以外的某个地方,例如,放置到 /usr/local/bin。这种做法唯一的弊病就是您现在需要在所有含有 php 标记符文件的第一行添加类似于以下的内容:
#!/usr/local/bin/php
您需要使得这些文件成为可执行文件。也就是说,像对待任何其它 cgi 脚本一样来对待它们。这些 cgi 脚本可能是用 perl、sh 或者任何其它使用的 #! shell-escape 机制来启动它们自身的脚本语言来编写的。要使这个方法中 php 能够正确处理 path_info 和 path_translated 信息,在编译 php 解析器时必须使用配置参数--enable-discard-path。下面是一个能作为cgi程序运行的php脚的例子。
#!/usr/local/bin/phpecho this is a my small program
4、最后将php请升级为最新稳定版本:4.3.8。
在php 4.1中,添加了一组特殊数据以访问外部数据。这些数组可以在任何范围内调用,这使得外部数据的访问更方便。在php 4.3.8中,register_globals被默认关闭以鼓励使用这些数组以避免无经验的开发者编写出不安全的php代码。作出这样的变化是出于安全性的考虑的。链接:http://www.php.net/downloads.php
二、 安全使用php
1、安装安全模块
php 的安全模式是为了试图解决共享服务器(shared-server)安全问题而设立的。在结构上,试图在 php 层上解决这个问题是不合理的,但修改 web 服务器层和操作系统层显得非常不现实。因此许多人,特别是 isp,目前使用安全模式。
表-1.安全模式配置指令
名称 默认值 类型
safe_mode 0 布尔型
safe_mode_gid 0 布尔型
safe_mode_include_dir null 字符串
safe_mode_exec_dir 字符串
safe_mode_allowed_env_vars php_ 字符串
safe_mode_protected_env_vars ld_library_path 字符串
disable_functions 字符串
disable_classes 字符串
open_basedir null 字符串
以下是该配置选项的简要解释。
(1)safe_mode boolean
是否启用 php 的安全模式。
(2)safe_mode_gid
默认情况下,安全模式在打开文件时会做 uid 比较检查。如果你想将其放宽到 gid 比较,则打开 safe_mode_gid。是否在文件访问时使用 uid(false)或者 gid(true)来做检查。
(3)safe_mode_include_dir
当从此目录及其子目录(目录必须在 include_path 中或者用完整路径来包含)包含文件时越过 uid/gid 检查。
从 php 4.2.0 开始,本指令可以接受和 include_path 指令类似的风格用分号隔开的路径,而不只是一个目录。
(4)safe_mode_exec_dir
如果 php 使用了安全模式,system() 和其它执行系统程序的函数将拒绝启动不在此目录中的程序。
(5)safe_mode_allowed_env_vars
设置某些环境变量可能是潜在的安全缺口。本指令包含有一个逗号分隔的前缀列表。在安全模式下,用户只能改变那些名字具有在这里提供的前缀的环境变量。默认情况下,用户只能设置以 php_ 开头的环境变量(例如 php_foo = bar)。
(6)safe_mode_protected_env_vars
本指令包含有一个逗号分隔的环境变量的列表,最终用户不能用 putenv() 来改变这些环境变量。甚至在 safe_mode_allowed_env_vars 中设置了允许修改时也不能改变这些变量。
(7)disable_functions
本指令允许你基于安全原因禁止某些函数。接受逗号分隔的函数名列表作为参数。 disable_functions 不受安全模式的影响。 本指令只能设置在 php.ini 中。例如你不能将其设置在 httpd.conf。
(8)disable_classes
本指令可以使你出于安全的理由禁用某些类。用逗号分隔类名。disable_classes 不受安全模式的影响。 本指令只能设置在 php.ini 中。例如你不能将其设置在 httpd.conf。该指令自 php 4.3.2 起可用。
(9)open_basedir
将 php 所能打开的文件限制在指定的目录树,包括文件本身。本指令不受安全模式打开或者关闭的影响。
2、以安全模式运行php
以安全模式运行php是使php脚本安全使用的好方法,特别是在允许用户使用自己开发的php脚本时。使用安全模式会使php在运行函数时检查是否存在安全问题。include、readfile、fopen、file、unlink、rmdir等等:被包含的文件或者该文件所在目录的所有者必须是正在运行的脚本的所有者; exec、system、passthm等:要执行的程序必须位于特定的目录(默认为/usr/local/php/bin)。编译php时可以用- -with-exe-dir选项设定这个值。
mysql_connect:这个函数用可选的用户名连接mysql数据库。在安全模式下,用户名必须是当前被执行的脚本的所有者,或运行httpd的用户名(通常是nobody)。
http authentication:包含http验证代码脚本所有者的用户id(数字型)会自动加到验证域。这样可以防止有人通过抓取密码的程序来欺骗同一个服务器上的http验证脚本。
3、使用用户识别和验证
有时需要唯一地确认一个用户。用户通常由请求和响应系统确认。用户名/口令组合就是这种系统的一个很好的例子,比如系统要求给出a1i的口令,响应的是ali的口令。这样验证是因为只有ali才知道这个口令。
(1)服务器端用户验征
这是用于服务端上对php程序要求最小的验证方法。只要让apache来管理对用户的验证就行了。
authname secret page authtype basic# the password file has been placed outside the web treeauthuserfile/home/car2002/website.pwrequirevalid-user
你需要把上述文件(文件名为.htaccess)放在需要保护的地方。用apache的htpasswd程序,可以建立包含用户名和口令组合的文件。把这个文件放在web目录树之外,只让该文件的拥有者查看和修改这个文件。当然,web服务器必须能够读取这个文件。
如果想读取被保护的目录,web服务器要求浏览器提供用户名和密码。浏览器弹出对话框,用户可以输入他们的用户名和密码。如果用户名和密码与口令文件中相符合,就允许用户读取被保护的页面;反之,将得到错误页面,告诉用户没有通过验证。被保护的域会显示出来以便用户知道输入那个用户名和密码。
(2)在php中进行用户识别和验证
和在apache服务器端进行用户识别和验证相比,在php进行用户识别和验证有以下优点:
a、 可注销。用户可以注销身份验证,这在apache无法实现。
b、 可失效。你可以在一定设计后让登陆失效。如用户登录后60分钟没有浏览你的网站,你可强制他们重新通过验证。
c、 可定制。例如可以用小的javaapplet加密要传送的口令,而在服务器上用mcryet库解密,这个工作可以在任何支持java的浏览器上进行。
d、 可基于数据库。你可以用保存在各种各样的数据库里的数据来验证用户,并且记录访问者访问网站的详细日志。
e、 可用于每个页面。你可在每个页面上决定是否需要验证,当然在apache服务器通过改变域也可以做到这一点。
f、 你也可以使浏览器弹出对话框。下面的例子显示了怎样从,mysql数据库中检索用名和口令:让用户填人用户名和口令。
only users with a working username/password combination can see this
例子:用 php 进行 http 认证
php 的 http 认证机制仅在 php 以 apache 模块方式运行时才有效,因此该功能不适用于 cgi 版本。在 apache 模块的 php 脚本中,可以用 header() 函数来向客户端浏览器发送authentication required信息,使其弹出一个用户名/密码输入窗口。
当用户输入用户名和密码后,包含有 url 的 php 脚本将会再次和预定义变量 php_auth_user、php_auth_pw 和 auth_type 一起被调用,这三个变量分别被设定为用户名,密码和认证类型。预定义变量保存在 $_server 或者 $http_server_vars 数组中。系统仅支持基本的认证。以下是在页面上强迫客户端认证的脚本范例:
(3) 检测ip地址
一般人们普遍认为一个ip地址唯一地确定一个访问者。但实际上并不是这样的。代理服务器可用相同的ip地址发送不同用户的请求。另外ip地址的盗用也普遍存在。检测 ip地址有它们的用处,但相当有限。例如你是一个论坛版主,你发现某个用户粘贴一些不健康的、违法的内容。你可以找到他的ip地址,把从这个ip连进来的用户逐出论坛。使用下面一行命令将会得到某个特定请求的源ip地址:
# ip = $remote_addr
4、使用php加密技术
在php中,加密技术主要用来加密信息、产生校验和和摘要。使用加密技术可大大地增强安全性能。 这里只讲述使用加密技术的一些概念。如果你想进一步了解,应参考一些好的加密技术资料。加密技术的标准是bmce schneier的应用加密技术,非常值得一读。他的网站(www.counterpane.com/labs.html )是在互联网上查找加密技术资料的好起点。数据加密是一个非常复杂的话题,这里只简单介绍一下。
现在关于加密php程序的,除了zend公司的产品产品,其他的都不尽人意。这里我给大家介绍一个开源的产品,一个很多好的php加密软件--php_screw. 他的好处是用了php的zend引擎,而且是对代码加密,和系统平台没有任何关系,解决了产品放在其他地方的保密问题。
(1)安装php_screw
#wget http://belnet.dl.sourceforge.net/sourceforge/php-screw/php_screw-1.3.tgz#tar zxvfphp_screw.1.3.tar.gz # cd php_screw.1.3# /opt/php/bin/phpize # ./configure --with-php-config=/opt/php/bin/php-config # vi my_screw.h#这个是密码文件,用户可以自己设置加密的密码 # make# cp ./modules/php_screw.so/usr/local/php/lib/php #cd tools # make #make生成screw 这个可执行文件,它用来加密php文件,生成加密后的.php文件。
(2)加密文件:
加密文件,就是用上一步的make命令生成的screw文件来生成新的加密文件。
举例:加密/opt/php/htdocs/index.htm文件
# ./screw /opt/php/htdocs/index.htm
加密程序会生成加密后的文件叫index.php,而原来加密前的文件自动备份成index.php.screw。 加密产品所要加密的文件后,可以删除备份文件。备份php_screw.so和screw文件,以后还可以继续使用他们来加密和解释。最后删除php_screw的目录。
5、使用具有ssl技术
ssl是英文server side includes的缩写。使用具有ssl(安全套接字协议层)功能的web服务器,可以不用改变一行代码而提高网站的安全性能。ssl使用加密方法来保护web服务器和浏览器之间的信息流。ssl不仅用于加密在互联网上传递的数据流,而且还提供双方身份验证。
这样,你就可以安全地在线购物而不必担心别人矢随你的信用卡的信息。这种特性使得ssl适用于那些交换重要信息的地方,像电子商务和基于web的邮件。
ssl使用公共密钥加密技术,服务器在连接结束时给客户端发送公用密钥用来加密信息,而加密的信息只有服务器用它自己持有的专用密钥才能解开。客户端用公用密钥加密数据,并且发送给服务端自己的密钥,以唯一确定自己,防止在系统两端之间有人冒充服务端或客户端进行欺骗。
加密的http连接用443端口号代替80端口号,以区别于普通的不加密的http。客户端使用加密http连接时会自动使用443端口而不是80端口。这使得服务端更容易作出相应的响应。
在apache服务器下,可以通过直接编辑服务器配置文件或者在需要使用ssi的目录中创建.htaccess文件来启动ssl。登录到服务器,找到配置文件的存放目录,使用文字编辑器打开文件srm.conf,找到以下几行:
# if you want to use server side includes, or cgi outside#scriptaliased directories,uncomment the following lines.#addtype text/x-server-parsed-html .shtml#addtype application/x-httpd-cgi.cgi
将以addtype开头的两行并且去掉每一行最前面的#符号即可。保存所做的修改,然后再打开文件access.conf。
<directory /usr/local/etc/httpd/htdocs> # this may also be none, all, or any combination of indexes,# includes, or followsymlinksoptions indexes followsymlinks</directory>
将其中的options indexes followsymlinks改为:options indexes followsymlinks includes 即可。
例子:如何建立一个仅接受强加密请求的ssl服务器,而又允许对外浏览器使用更强的加密?
这个功能被称为以服务器为网关的加密(server gated cryptography [sgc]), 在readme.globalid文档中有详细说明。简单地说就是:服务器拥有一个由来自verisign的一个特殊的ca证书签发的服务器身份证, 从而在对外浏览器上实现强加密。
其过程如下:
浏览器使用对外密码进行连接,服务器返回其全局id身份证,浏览器校验后在后继http通讯产生之前提升其密码组。
现在的问题是:如何允许这样的提升,而又强制性地使用强加密。换句话说就是:浏览器必须在开始连接时就使用强加密,或者提升倒强加密,但是维持对外密码是不允许的。以下巧妙地解决了这个问题:
httpd.conf:# 允许在初始握手阶段使用所有的密码,# 以允许对外服务器通过sgc功能提升密码组sslciphersuite all:!adh:rc4+rsa:+high:+medium:+low:+sslv2:+exp:+enull#但是最终会拒绝所有没有提升密码组的浏览器sslrequire %{ssl_cipher_usekeysize}>= 128
6、创建安全的php脚本
有很多编程技巧使php脚本更安全地运行。其中最重要的一条是使用一些安全常识。运行php比运行cgi脚本更安全,但它仍然有许多出现错误的地方。转换到安全运行模式能够限制出错所产生的结果。如果你的php脚本中有错误,可能会被人找到并且利用它来破坏站点甚至数据库。所以经常备份也是必要的。
(1)安全设置软件
基于web的应用程序,如在线目录,通常都在无人密切监视的情况下运行。如果发生错误时,你不可能立即采取行动。通常访问者最先注意到所发生的问题,你应该使他们很容易地报告所发生的问题。更进一步,可以由构成这个网站的脚本来跟踪这些问题。例如,你的访问者可能做一些你想不到的事情。也可能你对于重要函数所返回值没有检查,脚本可能会以不可预料的方式运行。
写出更加安全的程序,就可以避免这些问题。例如你应该检查数据库函数的返回值,如果数据库崩溃,显示给用户的应该是出错的信息页面而不是满屏幕的错误。你甚至可以让脚本在发生严重问题,如数据库崩溃、硬盘空间已满的时候自动通知你。你也应该检查从用户传来的所有数据。显然后者更重要。 如果你的程序能够应付各种错误,那么你的程序不仅更加可靠,而且可以花更少的时间来维护。这些时间可大大弥补你开发程序时所花的额外时间。
(2)合理存储和交换敏感信息
显然,你应尽量避免在互联网上以get、post、cookie或url编码的形式传递敏感信息,这样使信息很容易被窃取。使用支持ssl的web服务器能够做到这一点,因为它加密站点和访问者浏览器之间所有的信息流。
如果你没有支持ssl的web服务器,那么你需要其他的办法。比如没有必要总是发送数据到浏览器;把数据保存在数据库中,只向浏览器发送关键字,这样也很容易查找到所需要的数据;并以加密的形式发送所有的数据等等。实现这种功能的最简单的办法是使用session。php4支持本地化的session功能,php3则要使用phpmb库。
http 协议是一种无状态协议,它不负责为好连接的状态信息,因此无法跟踪客户端的各种信息,session的出现改变这一状况。当用户浏览一个支持session功能的cgi脚本时,在他离开这个网页前可以将用户信息保存在同意session id之下,也就是可以在不同的网页之间偕同存取用户信息。
如果不使用php的安全模式或在suexec下以cgi方式运行php,那么监视你的文件的内容就不可能实现。此时唯一防止别人读取数据的方法是尽快把数据保存到数据库中。
(3)检查用户输入
per1语言有个特性叫污点检测(taint checking)。当污点检测生效时,即使没有发生重大错误,你也不能运行含有可疑变量的函数。一个变量,当它的值是用户提供数据的一部分或全部时就变成可疑的了,因为这些数据被认为是不安全的。这样可提高系统安性。
php没有这个特性,但php有escapeshellcmd函数,可以达到同样的效果。另一个不让用户滥用脚本的方法是只允许使用经过严格检查的输入。例如如果需要输入ip地址时,应当检查这个ip地址十分有效。使用正则表达式可以做到这一点。下面这个函数例子将验证一个字符串这是否包含有效的ip地址。
function validate_ip($ip) {if (is_string($ip) && ereg(,^([0-9]{1,3})\.( [0-9]{l,3})\. ''([0-9]{1,3})\.([0-9]{1,3})$',$ip, $part)) {if ($part[l]
许多php程序的安全缺陷不是程序语言本身,而是没有在编码中考虑到用户提交数据的安全。所以对于用户提交数据,应当小心检查代码以确保任何由web浏览器提交的变量经过了相应的检查。下面是一个危险的变量使用:
三、 操作系统自身的补丁
给系统打已知漏洞的补丁是系统管理员最基本的职责,这也是最后一道防线。经过以上的配置,虽然说不上固若金汤,但是也在相当程度上给攻击者的测试造成很多麻烦,即使php脚本程序出现比较严重的漏洞,攻击者也无法造成实际性的破坏。
四、 总结
以上介绍了php的安全策略、加密和ssl的应用,以及任何用php进行身份验证识别。彻底安全的系统从理论上讲不可能,因此我们所指安全性只是在代价与可用性间作平衡。若是用户提交的每一个变量都要求有生物学验证(如指纹鉴定),则将获得极高水平的可靠性。
但是也会造成用户填写一个表格就要几十分钟。这时用户就会采取绕过安全验证的方法。一个系统的可靠性只能由整个链条中最薄弱的环节来决定。在任何安全系统里面,人是最脆弱的连接,单单技术本身不能让系统安全。
最好的安全策略通常能够不那么明显地适应环境的需求,它不会妨碍用户完成他们的工作,也不会使代码编写员面过分负担复杂的情形。
php作为开放源代码软件,开放的模式是双刃剑。从机制上讲,全世界的开发人员都能获得源代码,从而找出其中的纰漏,似乎安全性应该更好;但是同时,如果网络管理人员不能及时更新版本,也会留下安全隐患。
而且,影响安全的因素有很多。从编程水平到用户的使用水平等等,都将影响到系统的安全。仅仅通过开放或者封闭源代码,都不能从根本上解决安全问题。如果你是一个php程序员,你经常需要上相应的网站看,是否有补丁,是否有了bug fix,是否需要升级。千万不要报侥幸心理,否则一个shell脚本就可能拿下你的网站。套用一句名言:你的服务器永远可能在第二天被黑客接管。
php服务器运行的软件主要包括:samba,ftp,telnet,ssh,mysql,apache,mozilla等,这些软件,大都是开源软件,而且都在不停升级,稳定版和测试版交替出现。在www.samba.org和 www.apache.org 上,最新的changelog中都写着:bug fix, security bug fix的字样。所以php网管员要经常的关注相关网站的bug fix和升级,及时升级或添加补丁。本文希望php能够为您找到您真正需要的自由与安全的结合点。