Netanel Rubin的Perl Jam马戏团

我刚刚观看了Netanel Rubin在今年的混沌通信大会上的Perl Jam 2演讲。由于他将在Black Hat Asia上做同样的演讲,我认为有必要澄清他对Perl的某些说法(其他人已经这样做了)。他提出了三个主要观点

  1. Perl语言是不安全的
  2. Bugzilla和CGI.pm代表了典型的Perl编程方式
  3. Perl没有进步

我将逐一反驳这些观点,并说明为什么它们是错误的。在我看来,Perl仍然是一种强大、通用型的语言,非常适合构建动态Web应用程序、处理大数据和系统管理等工作。

观点1:Perl语言是不安全的

函数声明无法指定参数的数据类型

Netanel Rubin,Perl Jam 2

这并不正确。自2008年以来,Perl已经支持子程序签名,并使用Method::Signatures模块进行类型检查。自2006年以来,Moose对象系统提供了一套完整的类型系统和元对象编程接口(还有MooseX::Method::Signatures)。

开发者将散列和数组视为“安全”的数据类型……这是Perl的标准。你并不是被期望使用它,你必须这样做,因为你没有其他选择。这种安全问题是语言的一个基本组成部分。

Netanel Rubin,Perl Jam 2

Netanel首先描述了污染模式,并声称散列非常安全,散列键可以绕过Perl的污染检查。确实,散列键永远不会被认为是污染的。这在perlsec中有记录,并在精通Perl的第二章中进行了深入讨论。但这并不是因为散列被认为是安全的,而是因为将散列键标记为污染会带来巨大的性能损失。他从未解释为什么数组被认为是安全的。

Perl的ref函数是一种可靠且安全的方法,用于确定引用的数据类型。传递给函数的参数总是通过@_作为标量数组传递。毫无疑问,没有歧义。这不是安全方面的要求,但如果你想使用它们,你可以在Perl中使用函数签名、类型和元对象编程。这些功能已经可用多年。

但是,我感觉所有这些观点如果没有一个极端的Perl荒谬例子,可能会被忽视。因此,我找到了一个极端的例子。这个例子将清楚地展示语言的荒谬性。

Netanel Rubin,Perl Jam 2

这是有漏洞的代码,来自一个示例CGI应用程序

use strict;
use warnings;
use CGI;

my $cgi = CGI->new;

if ($cgi->upload( 'file' )) {
  my $file = $cgi->param( 'file' );
  while (<$file>) {
    print "$_";
  }
}

这个代码的问题在于,如果$file具有ARGV的值,菱形运算符<$file>将对@ARGV中的每个值调用open。CGI将@ARGV填充为HTTP查询参数,从而创建漏洞。因此,如果HTTP查询参数是ls|,Perl将执行ls。如果CGI程序在污染模式下运行,这种攻击向量将失败。无论如何,这是一个众所周知的风险,PLEAC项目的Perl建议从1999年开始显示如何正确解析CGI参数中的文件描述符(例如19.4)。Shishir Gundavaram的O'Reilly出版的《Web上的CGI编程》一书建议解析用户输入中的元字符,如|,这也可以防止此类攻击。这本书是在1996年出版的。

管道打开行为在openperlipcperlsec中都有很好的文档记载。在Mastering Perl的第2章中也有涉及。当您想要高效处理来自外部命令的大量数据时,这是一个非常有用的特性:就像shell管道一样,它会在Perl程序和外部二进制文件之间创建一个套接字,从而避免了需要一次性将整个输出读入内存的需求。

Netanel还在Bugzilla中识别了一个SQL注入漏洞。这个弱点是由一个编写不当的函数引起的,该函数未能正确验证动态SQL查询中使用的输入。开发者本应使用更安全的通过参数传递的DBI prepareexecute函数。

在这两种情况下,Perl都提供了安全解析不受信任输入的方法,但开发者没有使用它们。

主张2:Bugzilla & CGI.pm代表典型的Perl

像每个其他的Perl项目一样,Bugzilla大量使用了对待标量和非标量参数类型差异很大的函数。

Netanel Rubin,Perl Jam 2

Netanel在他的演讲中提到了这段代码,这段代码使用参数类型来决定要执行的操作

sub test {
  $arg1 = @_; # Get an argument

  if (ref $arg1 eq 'HASH')
    print $arg1{'key'};
  else
    print $arg1;
}

除了Netanel的代码中存在一个重大错误,这意味着它永远不会正常工作的事实外,认为每个其他的Perl项目都是这样编写的这种说法是荒谬的。《Dist::Zilla》是一个流行的Perl项目,拥有超过20,000行代码。你能猜到Dist::Zilla使用Netanel所描述的结构的频率吗?快速grep代码显示没有实例。Bugzilla是在1998年开发的,它不是现代Perl的例子。

关于CGI.pm,我无法比官方文档说得更好

CGI.pm已被从Perl核心中移除

这一决定的原因是,CGI.pm不再被认为是开发Web应用(包括快速原型和简单的Web脚本)的良好实践。在当前这个时间点,有更好的、更简洁、更快、更安全、更可扩展、更现代的替代方案可用。

CGI.pm文档

主张3:Perl没有改进

在演讲的问答环节,当一位观众说

我们在工作中几乎所有模块都使用Perl,并且它运行得非常好。我不知道为什么你会选择Perl作为攻击的语言。它是一种非常古老的语言,每个语言都有问题,这并不意味着……你必须停止使用它。

观众,Perl Jam 2

Netanel回应道

C语言受到批评并得到了改进。PHP受到批评并得到了改进。为什么Perl不能受到批评呢?……为什么他们不改进语言呢?

Netanel Rubin,Perl Jam 2

有趣的是,Perl一直在不断改进。每年都会有一个主要版本的Perl发布,为语言带来新功能和改进(见历史)。去年的版本包括了一个新的操作符,双菱形操作符<< >>,它禁用了前面显示的管道打开行为。CGI.pm在2014年5月从Perl核心模块列表中移除。这两件事情都发生在Netanel的演讲之前。

除了等待重大版本发布里程碑之外,Perl开发团队如果需要,可以在小版本中修复关键的安全问题(例如,参见5.16.3)。

Perl 还拥有强大的工具链来评估 Perl 代码。 Perl::Critic 是一个代码检查工具,用于检查 Perl 代码是否符合推荐的编码规范。甚至还有一个 策略 来检查潜在的 SQL 注入漏洞。

结论

你不能总是生活在对不知道要处理的数据类型的恐惧中……不相信你的散列,不相信你的数组,接下来,不相信你自己的代码吗?

Netanel Rubin,Perl Jam 2

作为一名有着多年编写专业 Perl 代码经验的人,并且与 Perl 程序员一起工作,我根本不认同这种经验。Netanel 所展示的只是一些被忽视的模块中的示例代码的攻击,以及一个遗留应用程序中的 SQL 注入漏洞。

Ruby 语言是否应该为 Ruby-on-Rails 中的漏洞负责?PHP 是否不安全,因为发现了超过 950 个针对 WordPress 的漏洞?这也不是动态语言的问题;在 静态语言与动态语言:文献综述 中,作者 Dan Luu 发现几乎没有证据表明静态类型语言比动态类型语言更安全。

每次你未能充分解析不受信任的输入,你都会有一个糟糕的日子。责怪 Perl 导致开发者编写了糟糕的代码,就像责怪字母表导致了 50 Shades of Grey 一样。


本文最初发布在 PerlTricks.com 上。

标签

David Farrell

David 是一名专业程序员,他经常 推文博客 关于代码和编程的艺术。

浏览他们的文章

反馈

这篇文章有什么问题吗?请通过在 GitHub 上打开一个 issue 或 pull request 来帮助我们。