澳门新浦京电子游戏编写PHP的安全策略

本文所讨论的安全性环境是在Linux+Apache+Mysql+PHP。超出此范围的安全性问题不在本文范畴之内

PHP最初是被称作Personal Home
Page,后来随着PHP成为一种非常流行的脚本语言,名称也随之改变了,叫做Professional
HyperText PreProcessor。以PHP4.2为例支持它的WEB服务器有:Apache,
Microsoft Internet information Sereve, Microsoft Personal web
Server,AOLserver,Netscape Enterprise 等等。

以CGI模式安装

CGI、FastCGI模式
CGI是通用网关(Common Gateway
Interface)接口,简单说就是连接Web服务器中的执行程序和网页资源的一座桥,它把网页接收的请求交给服务器的执行程序,再把服务器执行程序的结果返还给网页。每个请求到来时,都会先创建一个CGI的子进程,处理请求,结束子进程。以执行php请求为例,CGI会重新解析php.ini、重新载入全部dll扩展并重初始化全部数据结构,解释执行php脚本,如果请求的数量大时,频繁的反复加载CGI子进程会大量挤占系统资源和内存,导致性能低下。这时候就有了CGI的升级版本:FastCGI模式,它像一个常驻型的CGI,只要启动后,就会有固定数量的CGI进程在那里等待接收请求并执行,执行完请求之后并不结束进程,而是把这个进程放回可用的进程池里面继续等待新的请求。CGI的进程有动态伸缩性质,它启动时自动fork一个进程池,会选出一个空闲的进程执行新的请求,当进程池没有空闲进程时,会fork出新的子进程执行,但是在php-fpm.conf里面有配置cgi进程数量的参数,设置了最大进程数和最小进程数,当请求数量超过最大进程数时,php就不能执行新的请求了。

CGI模式安装可能会受到的攻击
CGI模式安装通常会把php的可执行文件安装到web服务器的cgi-bin目录,这样在访问系统文件时可以把cgi-bin目录添加在url里面,栗子:http://my.host/cgi-bin/php?/etc/passwd
?后面的参数表示php的命令行参数,请求这个URL,web服务器会执行 php
/etc/passwd
命令,这样一来,我想要运行服务器上的那个脚本,只需要知道脚本所在的位置并把它加在url里面,就可以任意执行了,这样是很不安全的。下面介绍几种方式可以减少这种攻击的伤害。

一、apache server安全性设置

PHP是一种功能强大的语言和解释器,无论是作为模块方式包含到web服务器里安装的还是作为单独的CGI程序程序安装的,都能访问文件、执行命令或者在服务器上打开链接。而这些特性都使得PHP运行时带来安全问题。虽然PH
P是特意设计成一种比用Perl或C语言所编写的CGI程序要安全的语言,但正确使用编译时和运行中的一些配置选项以及恰当的应用编码将会保证其运行的安全性。

1、权限控制

在每个脚本所在的目录都设置一个检查权限的脚本,如果有权限访问,再执行脚本。栗子:访问http://my.host/cgi-bin/php/secret/doc.html
想要访问/secret/doc.html,创建http://my.host/cgi-bin/php/secret/check.php的重定向,先从这个脚本检查权限,通过验证再去执行脚本。

1、以Nobody用户运行

一般情况下,Apache是由Root 来安装和运行的。如果Apache
Server进程具有Root用户特权,那么它将给系统的安全构成很大的威胁,应确保Apache
Server进程以最可能低的权限用户来运行。通过修改httpd.conf文件中的下列选项,以Nobody用户运行Apache
达到相对安全的目的。

User nobody
Group# -1

一、安全从开始编译PHP开始。

2、使用 –enable-force-cgi-redirect 选项

在编译PHP的configure脚本中指定参数:
–enable-force-cgi-redirect,可以防止任何人通过http://my.host/cgi-bin/php/secretdir/script.php这样的URL直接调用php,HP
在此模式下只会解析已经通过了 web 服务器的重定向规则的 URL。

2、ServerRoot目录的权限

为了确保所有的配置是适当的和安全的,需要严格控制Apache
主目录的访问权限,使非超级用户不能修改该目录中的内容。Apache
的主目录对应于Apache Server配置文件httpd.conf的Server
Root控制项中,应为:

Server Root /usr/local/apache

在编译PHP之前,首先确保操作系统的版本是最新的,必要的补丁程序必须安装过。另外使用编译的PHP也应当是最新的版本,关于PHP的安全漏洞也常有发现,请使用最新版本,如果已经安装过PHP请升级为最新版本:4.2.3

3、设置 doc_root 或 user_dir

配置文件的 doc_root选项 或设置环境变量 PHP_DOCUMENT_ROOT可以定义php
脚本执行目录,如果设置了该项,那么php就只会解释 doc_root
目录下的文并且目录外的脚本不会被php解释器执行,但是user_dir除外。另外一个user_dir选项,当这个选项没有被设置时,doc_root是唯一决定哪里的脚本能被执行的选项。设置了这个选项,就会执行用户主目录下的user_dir子目录下的文件,栗子:设置user_dir=public_php,那么访问http://my.host/~user/doc.php,就会访问用户主目录下的public_php子目录下的doc.php文件。如果用户的主目录是/home/user,那么将会执行文件/home/user/public_php/doc.php。

3、SSI的配置

在配置文件access.conf 或httpd.conf中的确Options指令处加入Includes NO
EXEC选项,用以禁用Apache Server 中的执行功能。避免用户直接执行Apache
服务器中的执行程序,而造成服务器系统的公开化。

Options Includes Noexec

相关链接:

4、php解释器放在 web 目录以外

一个非常安全的做法就是把php解释器放在 web 目录外的地方,比如说
/usr/local/bin。这样做唯一不便的地方就是必须在每一个包含 PHP
代码的文件的第一行加上#!/usr/local/bin/php,还要将这些文件的属性改成可执行。

4、阻止用户修改系统设置

在Apache 服务器的配置文件中进行以下的设置,阻止用户建立、修改
.htaccess文件,防止用户超越能定义的系统安全特性。

AllowOveride None
Options None
Allow from all

然后再分别对特定的目录进行适当的配置。

安装编译PHP过程中要注意的3个问题:

以apache模块安装

当 PHP 以 Apache 模块方式安装时,它将继承 Apache
用户(通常为“nobody”)的权限。就是说,如果用php操作数据库,而数据库刚好没有做访问控制,apache用户是nobody,那么php也是用nobody访问数据库,一些恶意的脚本即使不提供用户名和密码就可以操作数据库了。

5、改变Apache 服务器的缺省访问特性

Apache
的默认设置只能保障一定程度的安全,如果服务器能够通过正常的映射规则找到文件,那么客户端便会获取该文件,如
host/~ root/ 将允许用户访问整个文件系统。在服务器文件中加入如下内容:

order deny,ellow
Deny from all

将禁止对文件系统的缺省访问。

1、只容许CGI文件从特定的目录下执行:首先把处理CGI脚本的默认句柄删除,然后在要执行CGI脚本的目录在
文件中加入ScriptAlias指令。

文件系统安全

如果我们的web服务提供这样一个功能:用户可以删除服务器上某些文件,只需要在表单中提交要删除的文件即可。下面是代码实现:
<pre>
$username = $_POST[‘user_submitted_name’];//要删除的文件目录
$userfile = $_POST[‘user_submitted_filename’];//要删除的文件名
$homedir = “/home/$username”;
unlink (“$homedir/$userfile”);
echo “The file has been deleted!”;
</pre>
如果用户提交的文件是/etc/passwd,而web服务器有权限可以删除这个文件,那么重要的文件就这么被删除了。可以用2个措施来防止这类问题:1、只给web用户很小的权限
2、检查所有提交上来的变量,下面是改进的脚本:
<pre>
$username = $_SERVER[‘REMOTE_USER’]; // 使用认证机制
$userfile = basename($_POST[‘user_submitted_filename’]);
$homedir = “/home/$username”;
$filepath = “$homedir/$userfile”;
if (file_exists($filepath) && unlink($filepath)) {
//检查用户要删除的是不是他自己目录下的文件
$logstring = “Deleted $filepath澳门新浦京电子游戏,n”;
} else {
$logstring = “Failed to delete $filepathn”;
}
$fp = fopen(“/home/logging/filedelete.log”, “a”);
fwrite ($fp, $logstring);
fclose($fp);
echo htmlentities($logstring, ENT_QUOTES);
</pre>
但是,如果用户用../etc/作为自己的用户名呢?这个时候还需要对文件名进行检查,检查是否是字母、数字、下划线的组合。

6、CGI脚本的安全考虑

CGI脚本是一系列可以通过Web服务器来运行的程序。为了保证系统的安全性,应确保CGI的作者是可信的。对CGI而言,最好将其限制在一个特定的目
录下,如cgi-bin之下,便于管理;另外应该保证CGI目录下的文件是不可写的,避免一些欺骗性的程序驻留或混迹其中;如果能够给用户提供一个安全性
良好的CGI程序的模块作为参考,也许会减少许多不必要的麻烦和安全隐患;除去CGI目录下的所有非业务应用的脚本,以防异常的信息泄漏。

#Addhadler cgi-script .cgi

数据库安全

数据库已经成为各个动态网站上的重要组成部分,各种用户隐私、敏感数据等都保存在数据库中,所以数据库安全尤为重要。下面介绍一下我们在使用数据库时应该注意哪些安全方面的问题。

7、SSL链接加密

以上这些常用的举措可以给Apache Server
一个基本的安全运行环境,显然在具体实施上还要做进一步的细化分解,制定出符合实际应用的安全配置方案。

ScriptAlias /cgi-bin/ “/usr/local/apache/cgi-bin/”

设计数据库

第一步一般都是创建数据库,当创建一个数据库的时候,会指定一个所有者来执行新建数据库的sql语句,只有所有者或超级用户才有权任意操作数据库,如果想让其他用户使用,必须授权。应用程序永远不要使用数据库所有者或超级用户帐号来连接数据库,因为这些帐号可以执行任意的操作,比如说修改数据库结构(例如删除一个表)或者清空整个数据库的内容。应该为应用程序的不同功能创建不同的数据库账号,并且只赋予他们功能所需要的对数据库对象操作的权限,避免同一个用户可以完成另一个用户的事。最好不要把所有的事务逻辑都用脚本实现,最好用视图(view)、触发器(trigger)或者规则(rule)在数据库层面完成。

二、PHP安全性设置

服务器并不能阻止所有的安全问题,例如程序漏洞问题、用户输入表单问题、PHP文件权限问题等。
也可以通过一些手段来迷惑黑客或者别有用心者。

Directory “/usr/local/apache/cgi-bin

连接数据库

把连接建立在 SSL 加密技术上可以增加客户端和服务器端通信的安全性,或者
SSH 也可以用于加密客户端和数据库之间的连接。

1、程序代码漏洞问题

很多 PHP 程序所存在的重大弱点并不是 PHP
语言本身的问题,而是编程者的安全意识不高而导致的。因此,必须时时注意每一段代码可能存在的问题,去发现非正确数据提交时可能造成的影响。

<?php 
    unlink ($evil_var); 
    fwrite ($fp, $evil_var); 
    system ($evil_var); 
    exec ($evil_var); 
?>

必须时常留意你的代码,以确保每一个从客户端提交的变量都经过适当的检查,然后问自己以下一些问题:

此脚本是否只能影响所预期的文件?

非正常的数据被提交后能否产生作用?

此脚本能用于计划外的用途吗?

此脚本能否和其它脚本结合起来做坏事?

是否所有的事务都被充分记录了?

在写代码的时候问自己这些问题,否则以后可能要为了增加安全性而重写代码了。注意了这些问题的话,也许还不完全能保证系统的安全,但是至少可以提高安全性。

还可以考虑关闭 register_globals,Magic_quotes
或者其它使编程更方便但会使某个变量的合法性,来源和其值被搞乱的设置。

AllowOverride None

加密存储模型

ssh/ssl只能加密客户端和服务端交换的数据,并不难保护数据库中已有的数据。所以创建自己的加密机制,在保存数据库之前,先把数据加密然后再存储,读数据时再解密。

2、用户输入表单问题

验证用户输入的任何数据,保证PHP代码的安全。

注意1:JS只是为了提高来访用户的体验而产生的,而不是验证的工具。因为任何一个来访的用户都可能会,也有可能无意间就禁用了客户端脚本的执行,从而跳过这层验证。所以我们必须在PHP的服务器端程序上检验这些数据。

注意2:不要使用$_SERVER[‘HTTP_REFERER’]这个超级变量来检查数据的来源地址,一个很小的菜鸟黑客都会利用工具来伪造这个变量的数据,尽可能利用Md5,或者rand等函数来产生一个令牌,验证来源的时候,验证这个令牌是否匹配。

Options None

SQL注入

最终执行的sql语句中一般都包含用户提交的数据,如果不对用户提交的数据进行验证的话会有很大的安全漏洞,栗子:一段实现数据分页显示的代码,也可以被用作创建一个超级用户(PostgreSQL系统)。
<pre>
$offset = $argv[0]; // 注意,没有输入验证!
$query = “SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET
$offset;”;
$result = pg_query($conn, $query);
</pre>
一般的用户只会点击$offset被赋值的“上一页”、“下一页”的链接,这样$offset只是一个数值,但是如果有人把下面的语句先经过urlencode()处理,再加入url中的话,那么他就可以创建一个超级用户了,一开始的”0”只是为了提供一个正确的偏移量以便补充完整原来的查询,”—“是sql中的注释,表示告诉sql解释器忽略后面的语句。
<pre>
0;
insert into pg_shadow(usename,usesysid,usesuper,usecatupd,passwd)
select ‘crack’, usesysid, ‘t’,’t’,’crack’
from pg_shadow where usename=’postgres’;
</pre>

3、PHP文件权限问题

PHP 被设计为以用户级别来访问文件系统,所以完全有可能通过编写一段 PHP
代码来读取系统文件如
/etc/passwd,更改网络连接以及发送大量打印任务等等。因此必须确保 PHP
代码读取和写入的是合适的文件。
请看下面的代码,用户想要删除自己主目录中的一个文件。假设此情形是通过 web
界面来管理文件系统,因此 Apache 用户有权删除用户目录下的文件。

<?php 
    $username = $_POST['user_submitted_name']; 
    $homedir = "/home/$username"; 
    $file_to_delete = "$userfile"; 
    unlink ("$homedir/$userfile"); 
    echo "$file_to_delete has been deleted!"; 
?>

既然 username
变量可以通过用户表单来提交,那就可以提交别人的用户名和文件名,并删除该文件。这种情况下,就要考虑其它方式的认证:

只给 PHP 的 web 用户很有限的权限。 -检查所有提交上来的变量。
-以下是更加安全的文件名和变量的验证和检查:

<?php 
    $username = $_SERVER['REMOTE_USER']; 
    $homedir = "/home/$username"; 

    if (!ereg('^[^./][^/]*$', $userfile)) 
        die('bad filename'); 

    if (!ereg('^[^./][^/]*$', $username)) 
        die('bad username'); 
?>

Order allow,deny

预防sql注入的方法

1、不要使用超级用户或所有者账号去连接数据库,要用权限被严格控制的账号
2、检查输入的数据格式是否正确
3、用settype()函数来转换数据类型,也可以用sprintf()把它格式化为数字。
4、使用数据库特定的敏感字符转义函数(比如 mysql_escape_string() 和
sql_escape_string())把用户提交上来的非数字数据进行转义。如果数据库没有专门的敏感字符转义功能的话
addslashes() 和 str_replace() 可以代替完成这个工作。
5、也可以选择使用数据库的存储过程和预定义指针等特性来抽象数库访问,使用户不能直接访问数据表和视图。但这个办法又有别的影响。
6、保存数据库的查询日志,虽然不能防止任何攻击,但利用日志可以跟踪到哪个程序曾经被尝试攻击过,方便分析问题。

4、隐藏PHP扩展名

一般而言,通过隐藏的手段提高安全性被认为是作用不大的做法。但某些情况下,尽可能的多增加一份安全性都是值得的。

一些简单的方法可以帮助隐藏
PHP,这样做可以提高攻击者发现系统弱点的难度。在 php.ini 文件里设置
expose_php = off ,可以减少他们能获得的有用信息。

另一个策略就是让 web 服务器用 PHP 解析不同扩展名。无论是通过 .htaccess
文件还是 Apache 的配置文件,都可以设置能误导攻击者的文件扩展名:

# 使PHP看上去像其它的编程语言

AddType application/x-httpd-php .asp .py .pl

# 使 PHP 看上去像未知的文件类型

AddType application/x-httpd-php .bop .foo .133t

# 使 PHP 代码看上去像HTML页面

AddType application/x-httpd-php .htm .html

要让此方法生效,必须把 PHP
文件的扩展名改为以上的扩展名。这样就通过隐藏来提高了安全性,虽然防御能力很低而且有些缺点。

Allow from all

错误报告

通常 PHP
所返回的错误提示都能帮助开发者调试程序,它会提出哪个文件的哪些函数或代码出错,并指出错误发生的在文件的第几行,这些就是
PHP
本身所能给出的信息,但是攻击者故意输入错误的数据,然后查看错误提示的类型和上下文,这样就可以收集服务器的信息以便寻找弱点。解决办法是在代码中利用
error_reporting() 函数来定位问题,使代码更安全。在发布程序之前,先打开
E_ALL
测试代码,可以帮你很快找到变量使用不当的地方。一旦准备正式发布,就应该把
error_reporting() 的参数设为 0 来彻底关闭错误报告或者把 php.ini 中的
display_errors 设为 off 来关闭所有的错误显示使代码不能被探测。

三、Mysql数据库安全性设置

PHP 本身并不能保护数据库的安全。下面的章节只是讲述怎样用 PHP
脚本对数据库进行基本的访问和操作。记住一条简单的原则:深入防御。保护数据库的措施越多,攻击者就越难获得和使用数据库内的信息。正确地设计和应用数据库可以减少被攻击的担忧。

/Directory

1、数据库设计问题

应用程序永远不要使用数据库所有者或超级用户帐号来连接数据库,因为这些帐号可以执行任意的操作,比如说修改数据库结构(例如删除一个表)或者清空整个数据库的内容。以下截图的用户设置是危险的。

澳门新浦京电子游戏 1

澳门新浦京电子游戏 2

应该为程序的每个方面创建不同的数据库帐号,并赋予对数据库对象的极有限的权限。仅分配给能完成其功能所需的权限,避免同一个用户可以完成另一个用户的事情。这样即使攻击者利用程序漏洞取得了数据库的访问权限,也最多只能做到和该程序一样的影响范围。

Directory “/home/*/public_html/cgi-bin”

2.数据库连接问题

把连接建立在 SSL 加密技术上可以增加客户端和服务器端通信的安全性,或者
SSH
也可以用于加密客户端和数据库之间的连接。如果使用了这些技术的话,攻击者要监视服务器的通信或者得到数据库的信息是很困难的。

AllowOverride None

3.数据库数据的加密

SSL/SSH 能保护客户端和服务器端交换的数据,但 SSL/SSH
并不能保护数据库中已有的数据。SSL 只是一个加密网络数据流的协议。

如果攻击者取得了直接访问数据库的许可(绕过 web
服务器),敏感数据就可能暴露或者被滥用,除非数据库自己保护了这些信息。对数据库内的数据加密是减少这类风险的有效途径,但是只有很少的数据库提供这些加密功能。

对于这个问题,有一个简单的解决办法,就是创建自己的加密机制,然后把它用在
PHP 程序内,最常见的例子就是把密码经过 MD5
加密后的散列存进数据库来代替原来的明文密码。

<?php 
$query = sprintf("INSERT INTO users(name,pwd) VALUES('%s','%s');", 
addslashes($username), md5($password)); 
$result = pg_query($connection, $query); 
$query = sprintf("SELECT 1 FROM users WHERE name='%s' AND pwd='%s';", 
addslashes($username), md5($password)); 
$result = pg_query($connection, $query); 
if (pg_num_rows($result) > 0) { 
echo 'Welcome, $username!'; 
} else { 
echo 'Authentication failed for $username.'; 
} 
?>

Options ExecCGI

4、SQL注入问题

直接 SQL 命令注入就是攻击者常用的一种创建或修改已有 SQL
语句的技术,从而达到取得隐藏数据,或覆盖关键的值,甚至执行数据库主机操作系统命令的目的。这是通过应用程序取得用户输入并与静态参数组合成
SQL 查询来实现的。下面将会给出一些真实的例子。

<?php 
$query = "SELECT id, name, inserted, size FROM products 
WHERE size = '$size' 
ORDER BY $order LIMIT $limit, $offset;"; 
$result = odbc_exec($conn, $query); 
?>

可以在原来的查询的基础上添加另一个 SELECT 查询来获得密码: union select
’1′, concat(uname||’-’||passwd) as name, ’1971-01-01′, ’0′ from
usertable; 假如上述语句(使用 ‘ 和 –)被加入到 $query
中的任意一个变量的话,那么就麻烦了。

这些攻击总是建立在发掘安全意识不强的代码上的。所以,永远不要信任外界输入的数据,特别是来自于客户端的,包括选择框、表单隐藏域和
cookie。就如上面的第一个例子那样,就算是正常的查询也有可能造成灾难。

永远不要使用超级用户或所有者帐号去连接数据库。要用权限被严格限制的帐号。
检查输入的数据是否具有所期望的数据格式。PHP
有很多可以用于检查输入的函数,从简单的变量函数和字符类型函数(比如
is_numeric(),ctype_digit())到复杂的 Perl
兼容正则表达式函数都可以完成这个工作。

如果程序等待输入一个数字,可以考虑使用 is_numeric()
来检查,或者直接使用 settype() 来转换它的类型,也可以用 sprintf()
把它格式化为数字。

一个更安全的防止SQL注入的分页显示方法:

<?php 
settype($offset, 'integer'); 
$query = "SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET $offset;"; 
$query = sprintf("SELECT id, name FROM products ORDER BY name LIMIT 20 OFFSET %d;", 
$offset); 
?>

Order allow,deny

Allow from all

/Directory

SriptAlias的第一个参数指明在Web中的可用相对路径,第二个参数指明脚本放在服务器的目录。应该对每个目录

别名都用Directory,这样可使得除系统管理员之外的人不知道Web服务器上CGI脚本的清单。

Directory允许用户创建自己的CGI脚本。也可用SriptAliasMatch,但Directory更容易使用。
允许用户创建自己

CGI脚本可能会导致安全问题,你可能不希望用户创建自己的CGI。
Apache默认配置是注释掉cgi―script的处理句柄,但有/cgi-bin目录使用SriptAlias和Directory指令。
你也可禁止CGI执行,但仍允许执行PHP脚本。

2.把PHP解析器放在web目录外

把PHP解析器放在Web目录树外是非常重要的做法。这样可以防止web服务器对PHP的解析器的滥用。特别是

不要把PHP解析器放在cgi-bin或允许执行CGI程序的目录下。然而,使用Action解析脚本是不可能的,因为用Action指令时,PHP解析器大多数要放在能够执行CGI的目录下只有当PHP脚本作为CGI程序执行时,才能把PHP解析器放在Web目录树之外。

如果希望PHP脚本作为CGI程序执行(这们可以把PHP解析器放在Web目录树之外),可以这样:

( 1)所有的PHP脚本必须位于能执行CGI程序的目录里。

( 2)脚本必须是可执行的(仅在UNIX/Linux机器里)。

(3)脚本必须在文件头包括PHP解析器的路径。

你可用下面命令使PHP脚本为可执行:

#chmod +x test.php4

这样使在当前目录下的文件名为test.PhP4的脚本变为可执行。
下面是一个能作为CGI程序运行的PHP脚的小例子。

#!/usr/local/bin/php

echo “This is a my small cgi program”

  1. 按Apache模块方式安装:

当将PHP作为Apache模块使用时,它将继承Apche的用户权限(一般情况下用户为“nobody”)。这一点对于安全性和

验证有不少影响。例如,使用PHP访问数据库,除非数据库支持内建的访问控制,将不得不设置数据库对于用户“nobody”

的可访问权限。这将意味着恶意的脚本在没有访问用户名和密码,也能访问并修改数据库。通过Apache验证来保护数据不被暴露,或者也可使用LDAP、.htaccess文件等设计自己的访问控制模型,并在PHP脚本中将此代码作为其中部分引入。
通常,一旦安全性建立,此处PHP用户(此情形即Apache用户)就风险大大降低了,会发现PHP护现在已被封禁了将可能的染毒文件写入用户目录的能力。
此处最常犯的安全性错误是赋予Apache服务器根(root)权限。
将Apache用户权限提升到根权限是极端危险的。可能会危及整个系统,因此要小心使用sudo,chroot安全隐患大的命令等。除非你对安全有绝对的掌握,否则不要让其以ROOT权限运行。

二、让PHP的使用更安全。

1、以安全模式运行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数据库。在安全模式下,用户名必须是当前被执行的脚本的所有者,或运行的用户名(通常是nobody)。

HTTP
Authentication:包含HTTP验证代码脚本所有者的用户ID(数字型)会自动加到验证域。这样可以防止有人通过抓取密码的程序来欺骗同一个服务器上的HTTP验证脚本。

2、使用 用户识别和验证

有时需要唯一地确认一个用户。用户通常由请求和响应系统确认。用户名/口令组合就是这种系统的一个很好的例子,比如系统要求给出A1i的口令,响应的是Ali的口令。这样验证是因为只有Ali才知道这个口令。

服务器端用户验征

这是用于服务端上对PHP程序要求最小的验证方法。只要让Apache来管理对用户的验证就行了。

AuthName “Secret page” # The realm

AuthType Basic

# The password file has been placed outside the web tree

AuthUserFile /home/car2002/website.pw

LIMIT GET POST

require valid-user

/LIMIT

你需要把上述文件(文件名为.htaccess)放在需要保护的地方。用Apache的htpasswd程序,可以建立包含用户名和口令组合的文件。把这个文件放在Web目录树之外,只让该文件的拥有者查看和修改这个文件。当然,Web服务器必须能够读取这个文件。

如果想读取被保护的目录,Web服务器要求浏览器提供用户名和密码。浏览器弹出对话框,用户可以输入他们的用户名和密码。如果用户名和密码与口令文件中相符合,就允许用户读取被保护的页面;反之,将得到错误页面,告诉用户没有通过验证。被保护的域会显示出来以便用户知道输入那个用户名和密码。

在PHP中进行用户识别和验证

和在Apache服务器端进行用户识别和验证相比,在PHP进行用户识别和验证有以下优点:

A、可注销。

B、可失效。如用户登录后40分钟没有浏览你的网站,你可强制他们重新通过验证。

C、可定制。

D、可基于数据库。你可以用保存在各种各样的数据库里的数据来验证用户,并且记录访问者访问网站的详细日志。

E、可用于每个页面。你可在每个页面上决定是否需要验证。

F、你也可以使浏览器弹出对话框。下面的例子显示了怎样从,MySQL数据库中检索用名和口令:让用户填人用户名和口令。

if(!isset($PHP_AUTH_USER)) {

Header(“WWW-authenticate: basic realm=”restricted area””);

Header( “HTTP/I.0 401 Unauthorized”);

echo “You failed to provide the correct password…”;

exit;

} else {

mysql_select_db(“users”) ;

$user_id = strtolower($PHP^AUTH_USER);

$result = mysql_query(“SELECT password FROM users ” .

“WHERE username = $username”) ;

$row = mysql_fetch_array($result) ;

if ($PHP_AUTH_PW != $row[“password”]) {

Header( “WWW-authenticate: basic realm=”restricted area”

Header( “HTTP/I.0 401 Unauthorized”);

echo “You failed to provide the correct password…” ;

exit;

}

}

Only users with a working username/password combination can see this

(3) 检测IP地址

一般人们普遍认为一个IP地址唯一地确定一个访问者。但实际上并不是这样的。代理服务器可用相同的IP地址发送不同用户的请求。另外IP地址的盗用也普遍存在。检测
IP地址有它们的用处,但相当有限。例如你是一个论坛版主,你发现某个用户粘贴一些不健康的、违法的内容。你可以找到他的IP地址,把从这个IP连进来的用户逐出论坛。使用下面一行命令将会得到某个特定请求的源IP地址:

# ip = $REMOTE_ADDR

4、使用PHP加密技术

在PHP中,加密技术主要用来加密信息、产生校验和和摘要。使用加密技术可大大地增强安全性能。
这里只讲述使用加密技术的一些概念。如果你想进一步了解,应参考一些好的加密技术资料。加密技术的标准是Bmce
Schneier的应用加密技术,非常值得一读。他的网站(
)是在互联网上查找加密技术资料的好起点。数据加密是一个非常复杂的话题,这里只简单介绍一下。

PHP中大多数的加密函数由mcrypt库和mhash库提供。你需要在系统中装上这两个库,在编译时加上–ith-mcrypt和–ith-hash选项。PHP从
3.013版本开始支持mcrypt库。

5、使用具有SSL技术

SSI是英文Server Side
Includes的缩写。使用具有SSL(安全套接字协议层)功能的web服务器,可以不用改变一行代码而提高网站的安全性能。SSI使用加密方法来保护web服务器和浏览器之间的信息流。SSL不仅用于加密在互联网上传递的数据流,而且还提供双方身份验证。这样,你就可以安全地在线购物而不必担心别人矢随你的信用卡的信息。这种特性使得SSL适用于那些交换重要信息的地方,像电子商务和基于Web的邮件。

SSL使用公共密钥加密技术,服务器在连接结束时给客户端发送公用密钥用来加密信息,而加密的信息只有服务器用它自己持有的专用密钥才能解开。客户端用公用密钥加密数据,并且发送给服务端自己的密钥,以唯一确定自己,防止在系统两端之间有人冒充服务端或客户端进行欺骗。

加密的HTTP连接用443端口号代替80端口号,以区别于普通的不加密的HTTP。客户端使用加密HTTP连接时会自动使用443端口而不是80端口。这使得服务端更容易作出相应的响应。

在Apache服务器下,可以通过直接编辑服务器配置文件或者在需要使用SSI的目录中创建.htaccess文件来启动SSI。登录到服务器,找到配置文件的存放目录,使用文字编辑器打开文件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- .CGI

将以AddType开头的两行并且去掉每一行最前面的”#”符号即可。保存所做的修改,然后再打开文件access.conf。

<Directory /usr/local/etc/> # This may also be “None”, “All”, or any
combination of “Indexes”, # “Includes”, or “FollowSymLinks” Options
Indexes FollowSymLinks </Directory> 将其中的Options Indexes
FollowSymLinks改为:Options Indexes FollowSymLinks Includes 即可。

6、使用Apache的suEXEC机制

通常CGI程序或PHP脚本只能以启动web服务器的用户权限来运行(通常为www或nobody),这样会出现的情况之一是可以读写和修改由另一个用户的CGI和PHP脚本生成的文件(如脚本和密码文件)。也可能使用户可以连接到其他用户的数据库,但这与数据库的配置有关。如MySQL的默认配置便是允许的,但可以通过强制数据库进行口令验证来弥补此不足。PHP的safe―mode减少了这些问题,但所有的脚本仍然以相同的用户标识运行。Apache可以解决这个问题。suEXEC(在执行前改变用户标识)是一个小工具,允许以任意用户标识运行CGI程序,当然也包括PHP脚本,但根用户除外。而且可以和UseDir和VirtualHost项一起使用。

所以suEXEC也叫CGI封装。这意味着在脚本运行之前它需要通过一系列规定的安全检查。随Apache2。0版发布的suEXEC有26个检查点。suEXEC能解决一些安全问题,同时允许用户开发和更安全地执行自己的脚本。但是suEXEC会降低服务性能,因为suEXEC只能运行在CGI版本的PHP上,而CGI版本比模块版本运行速度慢。原因是模块版本使用了线程,而使用CGI版本的是进程。在不同线程之间的环境转换和访问公用的存储区域显然要比在不同的进程之间要快得多。使用suEXEC的另外一个问题是它增加了编写和使用PHP脚本的难度。你要确保脚本能通过suEXEC的检验。否则,你的脚本不会被执行。我们建议在你对安全性能要求比较高时使用suEXEC
,为此你还要以牺牲速度为代价。

7、创建安全的PHP脚本

有很多编程技巧使PHP脚本更安全地运行。其中最重要的一条是使用一些安全常识。运行PHP比运行CGI脚本更安全,但它仍然有许多出现错误的地方。转换到安全运行模式能够限制出错所产生的结果。如果你的PHP脚本中有错误,可能会被人找到并且利用它来破坏站点甚至数据库。所以经常备份也是必要的。

安全设置软件

基于Web的应用程序,如在线目录,通常都在无人密切监视的情况下运行。如果发生错误时,你不可能立即采取行动。通常访问者最先注意到所发生的问题,你应该使他们很容易地报告所发生的问题。更进一步,可以由构成这个网站的脚本来跟踪这些问题。例如,你的访问者可能做一些你想不到的事情。也可能你对于重要函数所返回值没有检查,脚本可能会以不可预料的方式运行。

写出更加安全的程序,就可以避免这些问题。例如你应该检查数据库函数的返回值,如果数据库崩溃,显示给用户的应该是出错的信息页面而不是满屏幕的错误。你甚至可以让脚本在发生严重问题,如数据库崩溃、硬盘空间已满的时候自动通知你。你也应该检查从用户传来的所有数据。显然后者更重要。
如果你的程序能够应付各种错误,那么你的程序不仅更加可靠,而且可以花更少的时间来维护。这些时间可大大弥补你开发程序时所花的额外时间。

存储和交换敏感信息

显然,你应尽量避免在互联网上以GET、POST、cookie或URL编码的形式传递敏感信息,这样使信息很容易被窃取。使用支持SSL的web服务器能够做到这一点,因为它加密站点和访问者浏览器之间所有的信息流。

如果你没有支持SSL的Web服务器,那么你需要其他的办法。比如没有必要总是发送数据到浏览器;把数据保存在数据库中,只向浏览器发送关键字,这样也很容易查找到所需要的数据;并以加密的形式发送所有的数据等等。实现这种功能的最简单的办法是使用Session。
PHP4支持本地化的Session功能,PHP3则要使用PHPMB库。

HTTP
协议是一种无状态协议,它不负责为好连接的状态信息,因此无法跟踪客户端的各种信息,Session的出现改变这一状况。当用户浏览一个支持Session功能的CGI脚本时,在他离开这个网页前可以将用户信息保存在同意Session
ID之下,也就是可以在不同的网页之间偕同存取用户信息。

如果不使用PHP的安全模式或在suEXEC下以CGI方式运行PHP,那么监视你的文件的内容就不可能实现。此时唯一防止别人读取数据的方法是尽快把数据保存到数据库中。

检查用户输入

Per1语言有个特性叫污点检测(taint
checking)。当污点检测生效时,即使没有发生重大错误,你也不能运行含有可疑变量的函数。一个变量,当它的值是用户提供数据的一部分或全部时就变成可疑的了,因为这些数据被认为是不安全的。这样可提高系统安性。
PHP没有这个特性,但PHP有escapeshellcmd函数,可以达到同样的效果。另一个不让用户滥用脚本的方法是只允许使用经过严格检查的输入。

使用最新的PHP版本 4.2.xx

在很长一段时间内,PHP作为服务器端脚本语言的最大卖点之一就是会为从表单提交的值自动建立一个全局变量。在PHP
4.1中,PHP的制作者们推荐了一个访问提交数据的替代手段。在PHP
4.2中,他们取消了那种老的做法。在PHP
4.1中,添加了一组特殊数据以访问外部数据。这些数组可以在任何范围内调用,这使得外部数据的访问更方便。在PHP
4.2中,register_globals被默认关闭以鼓励使用这些数组以避免无经验的开发者编写出不安全的PHP代码。作出这样的变化是出于安全性的考虑的。

三、总结

彻底安全的系统从理论上讲不可能,因此我们所指安全性只是在代价与可用性间作平衡。若是用户提交的每一个变量都要求有生物学验证(如指纹鉴定),则将获得极高水平的可靠性。但是也会造成用户填写一个表格就要几十分钟。这时用户就会采取绕过安全验证的方法。一个系统的可靠性只能由整个链条中最薄弱的环节来决定。在任何安全系统里面,人是最脆弱的连接,单单技术本身不能让系统安全。

PHP
还处在不断发展的过程中,你需要经常关注他的安全信息。这里笔者推荐你经常关注安全焦点(
)和Packetstorm( )。

发表评论

电子邮件地址不会被公开。 必填项已用*标注