在 Perl 中验证路径
Validating a path in Perl
我正在做一个 class 的实验室,我有一个关于检查特定输入的 Perl 字符串的问题。
基本上我想做的是确保我从用户那里收到的输入类似于:
/home/[anything is valid]/memo
本实验的重点是防止教师提供给我们的简单程序中的路径名攻击。所以我想在使用它之前检查以确保用户提供的路径名符合这种格式。
我目前正在使用 Perl 中的 abs_path()
方法来获取传入字符串的绝对路径,但现在我需要确保绝对路径包含我上面的内容。
这是我想要实现的目标:
my $input = "localhost:8080/cgi-bin/memo.cgi?memo=/home/megaboz/memo/new_CEO";
my $memo = '/home/megaboz/memo/new_CEO';
my $pathName = abs_path($memo);
if($pathName ne "/home/[anything works here]/memo/[anything works here]") {
#throw an error
}
else {
#process input
}
有什么指点吗?
欢迎来到 regular expressions 的精彩世界,这是 Perl 擅长的领域。
让我们来看看如何构建其中之一。首先,我们通常使用正斜杠来表示正则表达式,即
/some-expression/
但是由于您的路径中有正斜杠,这样做会涉及一些混乱的字符串转义,因此我们将使用 m
替代分隔符。
m(some-expression)
现在,我们要从 /home/
开始,到 /memo
结束。您可以在上面的 link 中阅读所有关于不同语法的信息,但是在正则表达式中我们使用 ^
和 $
(称为锚点)分别表示字符串的开头和结尾.所以我们的正则表达式看起来像
m(^/home/SOMETHING/memo$)
现在开始中间的部分。我们希望一切都过去。您的通用“任何”正则表达式是一个点 .
,它匹配任何单个字符。我们可以应用 Kleene 星 *
,它表示“之前出现的任何东西的零个或更多”。所以 .*
一起说“零个或更多的东西”。
m(^/home/.*/memo$)
这是我们的正则表达式。要应用它,我们使用 =~
来询问“是否匹配”,或使用 !~
来询问“是否失败”。您的代码结构方式,我们要检查失败。
if ($pathName !~ m(^/home/.*/memo$)) {
...
} else {
...
}
正则表达式相当普遍,基本上可以在任何编程语言中使用,因此它绝对是一项值得拥有的技能(尽管 Perl 以其强大的正则表达式支持而闻名,因此您是正确的工具字符串匹配功能)。
你的问题遗漏了很多东西,所以我不得不做一些猜测。而且,由于 Whosebug 主要是关于其他人在阅读这些答案时遇到类似问题,因此其中一些可能不适用于您。此外,其中大部分是关于网络安全的,而不是 Perl 特有的。你想用任何语言经历同样的事情。
首先,你说“这里什么都能用”。不要让那成为事实。考虑虚拟父目录 ..
指定围绕目录结构移动:
/home/../../memo/../../../target.pl
您最终得到了一个您不想公开的文件。不仅如此,如果他们能够通过其他方式在正确的位置创建 memo 符号链接,他们也可以使用它来四处移动。也就是说,您无法仅通过查看路径来真正判断您将获得什么文件,因为符号链接(我猜也是硬链接)可以完全改变一切。如果 memo 是 /
的符号链接怎么办?
其次,永远不要让远程 CGI 用户告诉您文件的位置。这对他们来说太多了,无法为您做出决定。相反,看起来您将允许他们提供两样东西。第二个位置的目录和最后的东西。让他们单独指定这两件事:
https://localhost:8080/cgi-bin/memo.cgi?user=megaboz&thing=NewCEO
你仍然需要验证这两件事,但分开做比在一堆其他事情中间做要容易得多。而且,由于您正在从用户那里获取输入并将其映射到文件系统,因此您应该使用污点检查 (perlsec),这有助于您捕获在程序外部使用的用户输入。要清除值,请使用匹配项并捕获您允许的内容。我建议你不要试图在这里挽救任何坏数据。如果它不符合您的预期,return 一个错误。此外,最好指定您允许的内容,而不是提出您将禁止的所有内容:
#!perl -T
my( $user ) = however_you_get_CGI_params( 'user' ) =~ m/\A([a-z0-9]+)\z/i;
my( $thing ) = however_you_get_CGI_params( 'thing' ) =~ m/\A([a-z0-9]+)\z/i;
unless( defined $user and defined $thing ) { ... return some error ... }
现在,这并不意味着您现在在 $user
和 $thing
中的值是正确的。它们只是有效值。将它们映射到您需要获取的任何内容。由于您已经构建了一条路径,因此检查路径是否存在可能就足够了:
use File::Spec::Functions;
my $path = catfile( '/home', $user, 'memo', $thing );
unless( -e $path ) { ... return some error ... }
我正在做一个 class 的实验室,我有一个关于检查特定输入的 Perl 字符串的问题。
基本上我想做的是确保我从用户那里收到的输入类似于:
/home/[anything is valid]/memo
本实验的重点是防止教师提供给我们的简单程序中的路径名攻击。所以我想在使用它之前检查以确保用户提供的路径名符合这种格式。
我目前正在使用 Perl 中的 abs_path()
方法来获取传入字符串的绝对路径,但现在我需要确保绝对路径包含我上面的内容。
这是我想要实现的目标:
my $input = "localhost:8080/cgi-bin/memo.cgi?memo=/home/megaboz/memo/new_CEO";
my $memo = '/home/megaboz/memo/new_CEO';
my $pathName = abs_path($memo);
if($pathName ne "/home/[anything works here]/memo/[anything works here]") {
#throw an error
}
else {
#process input
}
有什么指点吗?
欢迎来到 regular expressions 的精彩世界,这是 Perl 擅长的领域。
让我们来看看如何构建其中之一。首先,我们通常使用正斜杠来表示正则表达式,即
/some-expression/
但是由于您的路径中有正斜杠,这样做会涉及一些混乱的字符串转义,因此我们将使用 m
替代分隔符。
m(some-expression)
现在,我们要从 /home/
开始,到 /memo
结束。您可以在上面的 link 中阅读所有关于不同语法的信息,但是在正则表达式中我们使用 ^
和 $
(称为锚点)分别表示字符串的开头和结尾.所以我们的正则表达式看起来像
m(^/home/SOMETHING/memo$)
现在开始中间的部分。我们希望一切都过去。您的通用“任何”正则表达式是一个点 .
,它匹配任何单个字符。我们可以应用 Kleene 星 *
,它表示“之前出现的任何东西的零个或更多”。所以 .*
一起说“零个或更多的东西”。
m(^/home/.*/memo$)
这是我们的正则表达式。要应用它,我们使用 =~
来询问“是否匹配”,或使用 !~
来询问“是否失败”。您的代码结构方式,我们要检查失败。
if ($pathName !~ m(^/home/.*/memo$)) {
...
} else {
...
}
正则表达式相当普遍,基本上可以在任何编程语言中使用,因此它绝对是一项值得拥有的技能(尽管 Perl 以其强大的正则表达式支持而闻名,因此您是正确的工具字符串匹配功能)。
你的问题遗漏了很多东西,所以我不得不做一些猜测。而且,由于 Whosebug 主要是关于其他人在阅读这些答案时遇到类似问题,因此其中一些可能不适用于您。此外,其中大部分是关于网络安全的,而不是 Perl 特有的。你想用任何语言经历同样的事情。
首先,你说“这里什么都能用”。不要让那成为事实。考虑虚拟父目录 ..
指定围绕目录结构移动:
/home/../../memo/../../../target.pl
您最终得到了一个您不想公开的文件。不仅如此,如果他们能够通过其他方式在正确的位置创建 memo 符号链接,他们也可以使用它来四处移动。也就是说,您无法仅通过查看路径来真正判断您将获得什么文件,因为符号链接(我猜也是硬链接)可以完全改变一切。如果 memo 是 /
的符号链接怎么办?
其次,永远不要让远程 CGI 用户告诉您文件的位置。这对他们来说太多了,无法为您做出决定。相反,看起来您将允许他们提供两样东西。第二个位置的目录和最后的东西。让他们单独指定这两件事:
https://localhost:8080/cgi-bin/memo.cgi?user=megaboz&thing=NewCEO
你仍然需要验证这两件事,但分开做比在一堆其他事情中间做要容易得多。而且,由于您正在从用户那里获取输入并将其映射到文件系统,因此您应该使用污点检查 (perlsec),这有助于您捕获在程序外部使用的用户输入。要清除值,请使用匹配项并捕获您允许的内容。我建议你不要试图在这里挽救任何坏数据。如果它不符合您的预期,return 一个错误。此外,最好指定您允许的内容,而不是提出您将禁止的所有内容:
#!perl -T
my( $user ) = however_you_get_CGI_params( 'user' ) =~ m/\A([a-z0-9]+)\z/i;
my( $thing ) = however_you_get_CGI_params( 'thing' ) =~ m/\A([a-z0-9]+)\z/i;
unless( defined $user and defined $thing ) { ... return some error ... }
现在,这并不意味着您现在在 $user
和 $thing
中的值是正确的。它们只是有效值。将它们映射到您需要获取的任何内容。由于您已经构建了一条路径,因此检查路径是否存在可能就足够了:
use File::Spec::Functions;
my $path = catfile( '/home', $user, 'memo', $thing );
unless( -e $path ) { ... return some error ... }