perl-security
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePerl Security Patterns
Perl安全编程模式
Comprehensive security guidelines for Perl applications covering input validation, injection prevention, and secure coding practices.
Perl应用的全面安全指南,涵盖输入验证、注入防护和安全编码实践。
When to Activate
适用场景
- Handling user input in Perl applications
- Building Perl web applications (CGI, Mojolicious, Dancer2, Catalyst)
- Reviewing Perl code for security vulnerabilities
- Performing file operations with user-supplied paths
- Executing system commands from Perl
- Writing DBI database queries
- 在Perl应用中处理用户输入
- 构建Perl Web应用(CGI、Mojolicious、Dancer2、Catalyst)
- 检查Perl代码中的安全漏洞
- 使用用户提供的路径执行文件操作
- 从Perl中执行系统命令
- 编写DBI数据库查询
How It Works
工作原理
Start with taint-aware input boundaries, then move outward: validate and untaint inputs, keep filesystem and process execution constrained, and use parameterized DBI queries everywhere. The examples below show the safe defaults this skill expects you to apply before shipping Perl code that touches user input, the shell, or the network.
从感知污染的输入边界开始,向外扩展:验证并清除输入的污染,限制文件系统和进程执行范围,在所有场景下使用DBI参数化查询。以下示例展示了在发布涉及用户输入、Shell或网络的Perl代码前,应遵循的安全默认规范。
Taint Mode
Taint模式
Perl's taint mode () tracks data from external sources and prevents it from being used in unsafe operations without explicit validation.
-TPerl的taint模式()会跟踪来自外部源的数据,防止其在未经过显式验证的情况下用于不安全操作。
-TEnabling Taint Mode
启用Taint模式
perl
#!/usr/bin/perl -T
use v5.36;perl
#!/usr/bin/perl -T
use v5.36;Tainted: anything from outside the program
受污染:来自程序外部的任何数据
my $input = $ARGV[0]; # Tainted
my $env_path = $ENV{PATH}; # Tainted
my $form = <STDIN>; # Tainted
my $query = $ENV{QUERY_STRING}; # Tainted
my $input = $ARGV[0]; # Tainted
my $env_path = $ENV{PATH}; # Tainted
my $form = <STDIN>; # Tainted
my $query = $ENV{QUERY_STRING}; # Tainted
Sanitize PATH early (required in taint mode)
提前清理PATH(taint模式下必需)
$ENV{PATH} = '/usr/local/bin:/usr/bin:/bin';
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
undefined$ENV{PATH} = '/usr/local/bin:/usr/bin:/bin';
delete @ENV{qw(IFS CDPATH ENV BASH_ENV)};
undefinedUntainting Pattern
清除污染的模式
perl
use v5.36;perl
use v5.36;Good: Validate and untaint with a specific regex
推荐:使用特定正则表达式验证并清除污染
sub untaint_username($input) {
if ($input =~ /^([a-zA-Z0-9_]{3,30})$/) {
return $1; # $1 is untainted
}
die "Invalid username: must be 3-30 alphanumeric characters\n";
}
sub untaint_username($input) {
if ($input =~ /^([a-zA-Z0-9_]{3,30})$/) {
return $1; # $1已清除污染
}
die "无效用户名:必须是3-30位字母数字或下划线\n";
}
Good: Validate and untaint a file path
推荐:验证并清除文件路径的污染
sub untaint_filename($input) {
if ($input =~ m{^([a-zA-Z0-9._-]+)$}) {
return $1;
}
die "Invalid filename: contains unsafe characters\n";
}
sub untaint_filename($input) {
if ($input =~ m{^([a-zA-Z0-9._-]+)$}) {
return $1;
}
die "无效文件名:包含不安全字符\n";
}
Bad: Overly permissive untainting (defeats the purpose)
不推荐:过度宽松的污染清除(失去意义)
sub bad_untaint($input) {
$input =~ /^(.*)$/s;
return $1; # Accepts ANYTHING — pointless
}
undefinedsub bad_untaint($input) {
$input =~ /^(.*)$/s;
return $1; # 接受任何内容——毫无作用
}
undefinedInput Validation
输入验证
Allowlist Over Blocklist
白名单优先于黑名单
perl
use v5.36;perl
use v5.36;Good: Allowlist — define exactly what's permitted
推荐:白名单——明确定义允许的内容
sub validate_sort_field($field) {
my %allowed = map { $_ => 1 } qw(name email created_at updated_at);
die "Invalid sort field: $field\n" unless $allowed{$field};
return $field;
}
sub validate_sort_field($field) {
my %allowed = map { $_ => 1 } qw(name email created_at updated_at);
die "无效排序字段:$field\n" unless $allowed{$field};
return $field;
}
Good: Validate with specific patterns
推荐:使用特定模式验证
sub validate_email($email) {
if ($email =~ /^([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,})$/) {
return $1;
}
die "Invalid email address\n";
}
sub validate_integer($input) {
if ($input =~ /^(-?\d{1,10})$/) {
return $1 + 0; # Coerce to number
}
die "Invalid integer\n";
}
sub validate_email($email) {
if ($email =~ /^([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,})$/) {
return $1;
}
die "无效邮箱地址\n";
}
sub validate_integer($input) {
if ($input =~ /^(-?\d{1,10})$/) {
return $1 + 0; # 转换为数字类型
}
die "无效整数\n";
}
Bad: Blocklist — always incomplete
不推荐:黑名单——始终存在遗漏
sub bad_validate($input) {
die "Invalid" if $input =~ /[<>"';&|]/; # Misses encoded attacks
return $input;
}
undefinedsub bad_validate($input) {
die "无效内容" if $input =~ /[<>"';&|]/; # 无法检测编码后的攻击
return $input;
}
undefinedLength Constraints
长度限制
perl
use v5.36;
sub validate_comment($text) {
die "Comment is required\n" unless length($text) > 0;
die "Comment exceeds 10000 chars\n" if length($text) > 10_000;
return $text;
}perl
use v5.36;
sub validate_comment($text) {
die "评论不能为空\n" unless length($text) > 0;
die "评论超过10000字符限制\n" if length($text) > 10_000;
return $text;
}Safe Regular Expressions
安全正则表达式
ReDoS Prevention
防止ReDoS攻击
Catastrophic backtracking occurs with nested quantifiers on overlapping patterns.
perl
use v5.36;当嵌套量词作用于重叠模式时,会发生灾难性回溯。
perl
use v5.36;Bad: Vulnerable to ReDoS (exponential backtracking)
不推荐:易受ReDoS攻击(指数级回溯)
my $bad_re = qr/^(a+)+$/; # Nested quantifiers
my $bad_re2 = qr/^([a-zA-Z]+)$/; # Nested quantifiers on class
my $bad_re3 = qr/^(.?,){10,}$/; # Repeated greedy/lazy combo
my $bad_re = qr/^(a+)+$/; # 嵌套量词
my $bad_re2 = qr/^([a-zA-Z]+)$/; # 针对字符类的嵌套量词
my $bad_re3 = qr/^(.?,){10,}$/; # 贪婪/惰性量词的重复组合
Good: Rewrite without nesting
推荐:重写为无嵌套的形式
my $good_re = qr/^a+$/; # Single quantifier
my $good_re2 = qr/^[a-zA-Z]+$/; # Single quantifier on class
my $good_re = qr/^a+$/; # 单个量词
my $good_re2 = qr/^[a-zA-Z]+$/; # 针对字符类的单个量词
Good: Use possessive quantifiers or atomic groups to prevent backtracking
推荐:使用占有量词或原子组防止回溯
my $safe_re = qr/^[a-zA-Z]++$/; # Possessive (5.10+)
my $safe_re2 = qr/^(?>a+)$/; # Atomic group
my $safe_re = qr/^[a-zA-Z]++$/; # 占有量词(5.10+)
my $safe_re2 = qr/^(?>a+)$/; # 原子组
Good: Enforce timeout on untrusted patterns
推荐:对不可信模式执行超时限制
use POSIX qw(alarm);
sub safe_match($string, $pattern, $timeout = 2) {
my $matched;
eval {
local $SIG{ALRM} = sub { die "Regex timeout\n" };
alarm($timeout);
$matched = $string =~ $pattern;
alarm(0);
};
alarm(0);
die $@ if $@;
return $matched;
}
undefineduse POSIX qw(alarm);
sub safe_match($string, $pattern, $timeout = 2) {
my $matched;
eval {
local $SIG{ALRM} = sub { die "正则表达式超时\n" };
alarm($timeout);
$matched = $string =~ $pattern;
alarm(0);
};
alarm(0);
die $@ if $@;
return $matched;
}
undefinedSafe File Operations
安全文件操作
Three-Argument Open
三参数Open
perl
use v5.36;perl
use v5.36;Good: Three-arg open, lexical filehandle, check return
推荐:三参数open、词法文件句柄、检查返回值
sub read_file($path) {
open my $fh, '<:encoding(UTF-8)', $path
or die "Cannot open '$path': $!\n";
local $/;
my $content = <$fh>;
close $fh;
return $content;
}
sub read_file($path) {
open my $fh, '<:encoding(UTF-8)', $path
or die "无法打开'$path':$!\n";
local $/;
my $content = <$fh>;
close $fh;
return $content;
}
Bad: Two-arg open with user data (command injection)
不推荐:双参数open结合用户数据(命令注入风险)
sub bad_read($path) {
open my $fh, $path; # If $path = "|rm -rf /", runs command!
open my $fh, "< $path"; # Shell metacharacter injection
}
undefinedsub bad_read($path) {
open my $fh, $path; # 如果$path = "|rm -rf /",会执行命令!
open my $fh, "< $path"; # Shell元字符注入
}
undefinedTOCTOU Prevention and Path Traversal
防止TOCTOU和路径遍历
perl
use v5.36;
use Fcntl qw(:DEFAULT :flock);
use File::Spec;
use Cwd qw(realpath);perl
use v5.36;
use Fcntl qw(:DEFAULT :flock);
use File::Spec;
use Cwd qw(realpath);Atomic file creation
原子化文件创建
sub create_file_safe($path) {
sysopen(my $fh, $path, O_WRONLY | O_CREAT | O_EXCL, 0600)
or die "Cannot create '$path': $!\n";
return $fh;
}
sub create_file_safe($path) {
sysopen(my $fh, $path, O_WRONLY | O_CREAT | O_EXCL, 0600)
or die "无法创建'$path':$!\n";
return $fh;
}
Validate path stays within allowed directory
验证路径是否在允许的目录内
sub safe_path($base_dir, $user_path) {
my $real = realpath(File::Spec->catfile($base_dir, $user_path))
// die "Path does not exist\n";
my $base_real = realpath($base_dir)
// die "Base dir does not exist\n";
die "Path traversal blocked\n" unless $real =~ /^\Q$base_real\E(?:/|\z)/;
return $real;
}
Use `File::Temp` for temporary files (`tempfile(UNLINK => 1)`) and `flock(LOCK_EX)` to prevent race conditions.sub safe_path($base_dir, $user_path) {
my $real = realpath(File::Spec->catfile($base_dir, $user_path))
// die "路径不存在\n";
my $base_real = realpath($base_dir)
// die "基础目录不存在\n";
die "路径遍历已拦截\n" unless $real =~ /^\Q$base_real\E(?:/|\z)/;
return $real;
}
使用`File::Temp`创建临时文件(`tempfile(UNLINK => 1)`),并使用`flock(LOCK_EX)`防止竞争条件。Safe Process Execution
安全进程执行
List-Form system and exec
列表形式的system和exec
perl
use v5.36;perl
use v5.36;Good: List form — no shell interpolation
推荐:列表形式——无Shell插值
sub run_command(@cmd) {
system(@cmd) == 0
or die "Command failed: @cmd\n";
}
run_command('grep', '-r', $user_pattern, '/var/log/app/');
sub run_command(@cmd) {
system(@cmd) == 0
or die "命令执行失败:@cmd\n";
}
run_command('grep', '-r', $user_pattern, '/var/log/app/');
Good: Capture output safely with IPC::Run3
推荐:使用IPC::Run3安全捕获输出
use IPC::Run3;
sub capture_output(@cmd) {
my ($stdout, $stderr);
run3(@cmd, \undef, $stdout, $stderr);
if ($?) {
die "Command failed (exit $?): $stderr\n";
}
return $stdout;
}
use IPC::Run3;
sub capture_output(@cmd) {
my ($stdout, $stderr);
run3(@cmd, \undef, $stdout, $stderr);
if ($?) {
die "命令执行失败(退出码$?):$stderr\n";
}
return $stdout;
}
Bad: String form — shell injection!
不推荐:字符串形式——Shell注入风险!
sub bad_search($pattern) {
system("grep -r '$pattern' /var/log/app/"); # If $pattern = "'; rm -rf / #"
}
sub bad_search($pattern) {
system("grep -r '$pattern' /var/log/app/"); # 如果$pattern = "'; rm -rf / #"
}
Bad: Backticks with interpolation
不推荐:反引号结合插值
my $output = ; # Shell injection risk
ls $user_dir
Also use `Capture::Tiny` for capturing stdout/stderr from external commands safely.my $output = ; # Shell注入风险
ls $user_dir
也可使用`Capture::Tiny`安全捕获外部命令的标准输出/标准错误。SQL Injection Prevention
SQL注入防护
DBI Placeholders
DBI占位符
perl
use v5.36;
use DBI;
my $dbh = DBI->connect($dsn, $user, $pass, {
RaiseError => 1,
PrintError => 0,
AutoCommit => 1,
});perl
use v5.36;
use DBI;
my $dbh = DBI->connect($dsn, $user, $pass, {
RaiseError => 1,
PrintError => 0,
AutoCommit => 1,
});Good: Parameterized queries — always use placeholders
推荐:参数化查询——始终使用占位符
sub find_user($dbh, $email) {
my $sth = $dbh->prepare('SELECT * FROM users WHERE email = ?');
$sth->execute($email);
return $sth->fetchrow_hashref;
}
sub search_users($dbh, $name, $status) {
my $sth = $dbh->prepare(
'SELECT * FROM users WHERE name LIKE ? AND status = ? ORDER BY name'
);
$sth->execute("%$name%", $status);
return $sth->fetchall_arrayref({});
}
sub find_user($dbh, $email) {
my $sth = $dbh->prepare('SELECT * FROM users WHERE email = ?');
$sth->execute($email);
return $sth->fetchrow_hashref;
}
sub search_users($dbh, $name, $status) {
my $sth = $dbh->prepare(
'SELECT * FROM users WHERE name LIKE ? AND status = ? ORDER BY name'
);
$sth->execute("%$name%", $status);
return $sth->fetchall_arrayref({});
}
Bad: String interpolation in SQL (SQLi vulnerability!)
不推荐:SQL中直接插值(SQL注入漏洞!)
sub bad_find($dbh, $email) {
my $sth = $dbh->prepare("SELECT * FROM users WHERE email = '$email'");
# If $email = "' OR 1=1 --", returns all users
$sth->execute;
return $sth->fetchrow_hashref;
}
undefinedsub bad_find($dbh, $email) {
my $sth = $dbh->prepare("SELECT * FROM users WHERE email = '$email'");
# 如果$email = "' OR 1=1 --",会返回所有用户
$sth->execute;
return $sth->fetchrow_hashref;
}
undefinedDynamic Column Allowlists
动态列白名单
perl
use v5.36;perl
use v5.36;Good: Validate column names against an allowlist
推荐:针对白名单验证列名
sub order_by($dbh, $column, $direction) {
my %allowed_cols = map { $_ => 1 } qw(name email created_at);
my %allowed_dirs = map { $_ => 1 } qw(ASC DESC);
die "Invalid column: $column\n" unless $allowed_cols{$column};
die "Invalid direction: $direction\n" unless $allowed_dirs{uc $direction};
my $sth = $dbh->prepare("SELECT * FROM users ORDER BY $column $direction");
$sth->execute;
return $sth->fetchall_arrayref({});}
sub order_by($dbh, $column, $direction) {
my %allowed_cols = map { $_ => 1 } qw(name email created_at);
my %allowed_dirs = map { $_ => 1 } qw(ASC DESC);
die "无效列名:$column\n" unless $allowed_cols{$column};
die "无效排序方向:$direction\n" unless $allowed_dirs{uc $direction};
my $sth = $dbh->prepare("SELECT * FROM users ORDER BY $column $direction");
$sth->execute;
return $sth->fetchall_arrayref({});}
Bad: Directly interpolating user-chosen column
不推荐:直接使用用户指定的列名
sub bad_order($dbh, $column) {
$dbh->prepare("SELECT * FROM users ORDER BY $column"); # SQLi!
}
undefinedsub bad_order($dbh, $column) {
$dbh->prepare("SELECT * FROM users ORDER BY $column"); # SQL注入风险!
}
undefinedDBIx::Class (ORM Safety)
DBIx::Class(ORM安全)
perl
use v5.36;perl
use v5.36;DBIx::Class generates safe parameterized queries
DBIx::Class会生成安全的参数化查询
my @users = $schema->resultset('User')->search({
status => 'active',
email => { -like => '%@example.com' },
}, {
order_by => { -asc => 'name' },
rows => 50,
});
undefinedmy @users = $schema->resultset('User')->search({
status => 'active',
email => { -like => '%@example.com' },
}, {
order_by => { -asc => 'name' },
rows => 50,
});
undefinedWeb Security
Web安全
XSS Prevention
XSS防护
perl
use v5.36;
use HTML::Entities qw(encode_entities);
use URI::Escape qw(uri_escape_utf8);perl
use v5.36;
use HTML::Entities qw(encode_entities);
use URI::Escape qw(uri_escape_utf8);Good: Encode output for HTML context
推荐:针对HTML上下文编码输出
sub safe_html($user_input) {
return encode_entities($user_input);
}
sub safe_html($user_input) {
return encode_entities($user_input);
}
Good: Encode for URL context
推荐:针对URL上下文编码
sub safe_url_param($value) {
return uri_escape_utf8($value);
}
sub safe_url_param($value) {
return uri_escape_utf8($value);
}
Good: Encode for JSON context
推荐:针对JSON上下文编码
use JSON::MaybeXS qw(encode_json);
sub safe_json($data) {
return encode_json($data); # Handles escaping
}
use JSON::MaybeXS qw(encode_json);
sub safe_json($data) {
return encode_json($data); # 自动处理转义
}
Template auto-escaping (Mojolicious)
模板自动转义(Mojolicious)
<%= $user_input %> — auto-escaped (safe)
<%= $user_input %> —— 自动转义(安全)
<%== $raw_html %> — raw output (dangerous, use only for trusted content)
<%== $raw_html %> —— 原始输出(危险,仅用于可信内容)
Template auto-escaping (Template Toolkit)
模板自动转义(Template Toolkit)
[% user_input | html %] — explicit HTML encoding
[% user_input | html %] —— 显式HTML编码
Bad: Raw output in HTML
不推荐:HTML中直接输出原始内容
sub bad_html($input) {
print "<div>$input</div>"; # XSS if $input contains <script>
}
undefinedsub bad_html($input) {
print "<div>$input</div>"; # 如果$input包含<script>则存在XSS风险
}
undefinedCSRF Protection
CSRF防护
perl
use v5.36;
use Crypt::URandom qw(urandom);
use MIME::Base64 qw(encode_base64url);
sub generate_csrf_token() {
return encode_base64url(urandom(32));
}Use constant-time comparison when verifying tokens. Most web frameworks (Mojolicious, Dancer2, Catalyst) provide built-in CSRF protection — prefer those over hand-rolled solutions.
perl
use v5.36;
use Crypt::URandom qw(urandom);
use MIME::Base64 qw(encode_base64url);
sub generate_csrf_token() {
return encode_base64url(urandom(32));
}验证令牌时使用恒定时长比较。大多数Web框架(Mojolicious、Dancer2、Catalyst)都提供内置的CSRF防护——优先使用框架自带方案而非自行实现。
Session and Header Security
会话与头部安全
perl
use v5.36;perl
use v5.36;Mojolicious session + headers
Mojolicious会话 + 头部配置
$app->secrets(['long-random-secret-rotated-regularly']);
$app->sessions->secure(1); # HTTPS only
$app->sessions->samesite('Lax');
$app->hook(after_dispatch => sub ($c) {
$c->res->headers->header('X-Content-Type-Options' => 'nosniff');
$c->res->headers->header('X-Frame-Options' => 'DENY');
$c->res->headers->header('Content-Security-Policy' => "default-src 'self'");
$c->res->headers->header('Strict-Transport-Security' => 'max-age=31536000; includeSubDomains');
});
undefined$app->secrets(['long-random-secret-rotated-regularly']);
$app->sessions->secure(1); # 仅HTTPS
$app->sessions->samesite('Lax');
$app->hook(after_dispatch => sub ($c) {
$c->res->headers->header('X-Content-Type-Options' => 'nosniff');
$c->res->headers->header('X-Frame-Options' => 'DENY');
$c->res->headers->header('Content-Security-Policy' => "default-src 'self'");
$c->res->headers->header('Strict-Transport-Security' => 'max-age=31536000; includeSubDomains');
});
undefinedOutput Encoding
输出编码
Always encode output for its context: for HTML, for URLs, for JSON.
HTML::Entities::encode_entities()URI::Escape::uri_escape_utf8()JSON::MaybeXS::encode_json()始终根据输出上下文进行编码:HTML上下文使用,URL上下文使用,JSON上下文使用。
HTML::Entities::encode_entities()URI::Escape::uri_escape_utf8()JSON::MaybeXS::encode_json()CPAN Module Security
CPAN模块安全
- Pin versions in cpanfile:
requires 'DBI', '== 1.643'; - Prefer maintained modules: Check MetaCPAN for recent releases
- Minimize dependencies: Each dependency is an attack surface
- 固定版本在cpanfile中:
requires 'DBI', '== 1.643'; - 优先选择维护中的模块:在MetaCPAN上检查最近的版本更新
- 最小化依赖:每个依赖都是一个攻击面
Security Tooling
安全工具
perlcritic Security Policies
perlcritic安全策略
ini
undefinedini
undefined.perlcriticrc — security-focused configuration
.perlcriticrc —— 安全导向的配置
severity = 3
theme = security + core
severity = 3
theme = security + core
Require three-arg open
要求使用三参数open
[InputOutput::RequireThreeArgOpen]
severity = 5
[InputOutput::RequireThreeArgOpen]
severity = 5
Require checked system calls
要求检查系统调用返回值
[InputOutput::RequireCheckedSyscalls]
functions = :builtins
severity = 4
[InputOutput::RequireCheckedSyscalls]
functions = :builtins
severity = 4
Prohibit string eval
禁止字符串形式的eval
[BuiltinFunctions::ProhibitStringyEval]
severity = 5
[BuiltinFunctions::ProhibitStringyEval]
severity = 5
Prohibit backtick operators
禁止反引号运算符
[InputOutput::ProhibitBacktickOperators]
severity = 4
[InputOutput::ProhibitBacktickOperators]
severity = 4
Require taint checking in CGI
CGI脚本要求启用污染检查
[Modules::RequireTaintChecking]
severity = 5
[Modules::RequireTaintChecking]
severity = 5
Prohibit two-arg open
禁止双参数open
[InputOutput::ProhibitTwoArgOpen]
severity = 5
[InputOutput::ProhibitTwoArgOpen]
severity = 5
Prohibit bare-word filehandles
禁止裸字文件句柄
[InputOutput::ProhibitBarewordFileHandles]
severity = 5
undefined[InputOutput::ProhibitBarewordFileHandles]
severity = 5
undefinedRunning perlcritic
运行perlcritic
bash
undefinedbash
undefinedCheck a file
检查单个文件
perlcritic --severity 3 --theme security lib/MyApp/Handler.pm
perlcritic --severity 3 --theme security lib/MyApp/Handler.pm
Check entire project
检查整个项目
perlcritic --severity 3 --theme security lib/
perlcritic --severity 3 --theme security lib/
CI integration
CI集成
perlcritic --severity 4 --theme security --quiet lib/ || exit 1
undefinedperlcritic --severity 4 --theme security --quiet lib/ || exit 1
undefinedQuick Security Checklist
快速安全检查清单
| Check | What to Verify |
|---|---|
| Taint mode | |
| Input validation | Allowlist patterns, length limits |
| File operations | Three-arg open, path traversal checks |
| Process execution | List-form system, no shell interpolation |
| SQL queries | DBI placeholders, never interpolate |
| HTML output | |
| CSRF tokens | Generated, verified on state-changing requests |
| Session config | Secure, HttpOnly, SameSite cookies |
| HTTP headers | CSP, X-Frame-Options, HSTS |
| Dependencies | Pinned versions, audited modules |
| Regex safety | No nested quantifiers, anchored patterns |
| Error messages | No stack traces or paths leaked to users |
| 检查项 | 验证内容 |
|---|---|
| Taint模式 | CGI/Web脚本是否添加 |
| 输入验证 | 白名单模式、长度限制 |
| 文件操作 | 三参数open、路径遍历检查 |
| 进程执行 | 列表形式的system、无Shell插值 |
| SQL查询 | DBI占位符、无直接插值 |
| HTML输出 | 使用 |
| CSRF令牌 | 已生成、在状态变更请求中验证 |
| 会话配置 | Secure、HttpOnly、SameSite cookies |
| HTTP头部 | CSP、X-Frame-Options、HSTS |
| 依赖项 | 固定版本、经过审计的模块 |
| 正则表达式安全 | 无嵌套量词、锚定模式 |
| 错误信息 | 未向用户泄露堆栈跟踪或路径 |
Anti-Patterns
反模式
perl
undefinedperl
undefined1. Two-arg open with user data (command injection)
1. 双参数open结合用户数据(命令注入)
open my $fh, $user_input; # CRITICAL vulnerability
open my $fh, $user_input; # 严重漏洞
2. String-form system (shell injection)
2. 字符串形式的system(Shell注入)
system("convert $user_file output.png"); # CRITICAL vulnerability
system("convert $user_file output.png"); # 严重漏洞
3. SQL string interpolation
3. SQL字符串插值
$dbh->do("DELETE FROM users WHERE id = $id"); # SQLi
$dbh->do("DELETE FROM users WHERE id = $id"); # SQL注入
4. eval with user input (code injection)
4. 使用用户输入执行eval(代码注入)
eval $user_code; # Remote code execution
eval $user_code; # 远程代码执行
5. Trusting $ENV without sanitizing
5. 信任未清理的$ENV
my $path = $ENV{UPLOAD_DIR}; # Could be manipulated
system("ls $path"); # Double vulnerability
my $path = $ENV{UPLOAD_DIR}; # 可能被篡改
system("ls $path"); # 双重漏洞
6. Disabling taint without validation
6. 未验证就清除污染
($input) = $input =~ /(.*)/s; # Lazy untaint — defeats purpose
($input) = $input =~ /(.*)/s; # 草率的污染清除——失去意义
7. Raw user data in HTML
7. HTML中直接输出用户原始数据
print "<div>Welcome, $username!</div>"; # XSS
print "<div>Welcome, $username!</div>"; # XSS风险
8. Unvalidated redirects
8. 未验证的重定向
print $cgi->redirect($user_url); # Open redirect
**Remember**: Perl's flexibility is powerful but requires discipline. Use taint mode for web-facing code, validate all input with allowlists, use DBI placeholders for every query, and encode all output for its context. Defense in depth — never rely on a single layer.print $cgi->redirect($user_url); # 开放重定向
**注意**:Perl的灵活性强大但需要严谨的规范。面向Web的代码启用taint模式,使用白名单验证所有输入,所有查询都使用DBI占位符,并根据上下文对所有输出进行编码。采用纵深防御——绝不依赖单一防护层。