Mik*_*e B 7 shell security awk shell-script quoting
根据对 stackoverflow的回答,我的理解是将 bash 变量封装在双引号中是一种相当安全的用户输入清理方式。
awk 变量呢?例如,如果我有类似的东西:
awk -v SOURCEIP="$SOURCEIP" -v REVERSEDNS="$REVERSEDNS" '{
gsub(/^_TMPSOURCEIP_/, SOURCEIP);
gsub(/^_TMPREVERSEDNS_/, REVERSEDNS);
print
}' /home/foo/footemplate
Run Code Online (Sandbox Code Playgroud)
我应该在 gsub 行中的变量周围加上引号吗?所以它看起来像:
awk -v SOURCEIP="$SOURCEIP" -v REVERSEDNS="$REVERSEDNS" '{
gsub(/^_TMPSOURCEIP_/, "SOURCEIP");
gsub(/^_TMPREVERSEDNS_/, "REVERSEDNS");
print
}' /home/foo/footemplate
Run Code Online (Sandbox Code Playgroud)
或者这没有区别?
(好吧,抱歉我读得太快了,所以我的一些回答有点离题,仍然保持原样,因为它可能对您或某些人有用)
这里有几件事情需要考虑。
在 POSIX shell 中(在列表上下文中,如在命令的参数中)不带引号的变量awk是 split+glob 运算符。
如果你这样做:
cmd foo=$var
Run Code Online (Sandbox Code Playgroud)
哪里$var是* *。
这不是要求 shell$var根据$IFS特殊 shell 变量的值拆分内容,默认情况下是空白。所以上面,这给了我们foo=*和*并在每个foo=*文件上执行 globbing,即扩展到当前目录中以开头的所有文件名foo=和*所有非隐藏文件名。
所以,实际上,你几乎总是应该引用你的shell变量,不管它们是否是参数awk。这也适用于 shell 命令替换 ( `...`and $(...)) 和 shell 算术扩展 ( $((...)))。
awk另一个问题是awk(不是外壳)在变量赋值中扩展反斜杠转义序列-v var=value (并且在 GNU awk4.2 或更高版本中,如果值以 开头@/并以 结尾/,则将其视为正则表达式类型的变量)。
例如,-v var='\n/\n/'将awk var变量的内容设置为<newline>/<newline>/,而不是\n/\n/。这也适用于awk定义为的变量:
awk '...' var=value
Run Code Online (Sandbox Code Playgroud)
要传递数据awk而不进行扩展,您可以使用ENVIRON或ARGVawk 数组:
var=$value awk 'BEGIN {var=ENVIRON["var"]} ...'
Run Code Online (Sandbox Code Playgroud)
(上面,它是一个shell变量赋值(到一个非数组变量),所以不能有split+glob,这是可以省略变量周围引号的罕见情况之一)
或者:
awk 'BEGIN {var=ARGV[1]; delete ARGV[1]} ...' "$value"
Run Code Online (Sandbox Code Playgroud)
awk变量split+glob 只是一个外壳(错误)功能。该awk语言是一种完全不同的语言。
在 中awk,变量被引用为 a varname,not$varname和引号用于引入字符串。字符串"varname"也是如此varname,而 whilevarname指的是变量。
严格来说,引用 shell 变量不是清理,它不是引用使用 split+glob 运算符的变量。虽然在大多数语言中,你在固定字符串周围加上引号,但在 shell 中,情况正好相反:一切都是字符串,引号用于防止某些特殊行为,尤其是变量应该几乎总是被引用(一种糟糕的设计决定)在 70 年代的 Bourne shell 中是有意义的,但在现代 shell 中是一个障碍,zsh它是唯一部分修复了该问题的 shell)。
shell 或 awk 不会评估/解释存储在它们自己的变量中的代码,除非你告诉他们这样做。
var='foo; rm -f var'
echo $var
# or
echo "$var"
Run Code Online (Sandbox Code Playgroud)
不会导致变量的内容被评估为 shell 代码(尽管第一个将进行拆分和通配,这可能会产生可怕的后果(例如使用var='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*')。您需要:
eval "echo $var"
# or
sh -c "echo $var"
Run Code Online (Sandbox Code Playgroud)
因为它被评估/解释为 shell 代码。
awk没有这样的eval功能。perl/python做。
但要注意交叉污染。您可以让 shell 将变量数据(在shell变量中)作为代码执行awk:
awk '{print "'"$var"': " $0}'
Run Code Online (Sandbox Code Playgroud)
如果$var shell变量包含例如:
var='test"; print "foo" > /etc/passwd; print "blah'
Run Code Online (Sandbox Code Playgroud)
因为 shell 会执行:
["awk", "{print \"test\"; print \"foo\" > /etc/passwd; print \"blah: \" $0}"]
Run Code Online (Sandbox Code Playgroud)
或者反过来:
awk '{system("echo foo: " $0)}' < file
Run Code Online (Sandbox Code Playgroud)
哪里awk会运行一个shell:
["sh", "-c", "echo foo: content-of-the-line"]
Run Code Online (Sandbox Code Playgroud)
对于每一行file(并想想一行; rm -rf /会做什么)。
它不仅在awk和之间sh。每当变量/不受控制的数据可能被另一个解释器评估为代码时,您必须小心。例子是:
sed "s/$regexp/blah/g"
Run Code Online (Sandbox Code Playgroud)
sed's 的语言是有限的,但它仍然可以造成伤害,就像regexp='//;w /etc/passwd; s/'.
或者:
find . -exec sh -c "echo {}" \;
Run Code Online (Sandbox Code Playgroud)
现在,为了避免这些问题,有两种通用方法:
将变量从一个解释器转换为另一个解释器。这适用于 shell -> awk 或 find -> sh 上面的情况。喜欢改变:
awk '{print "'"$var"': " $0}'
Run Code Online (Sandbox Code Playgroud)
到:
awk -v awk_var="$var" '{print awk_var ": " $0}'
Run Code Online (Sandbox Code Playgroud)
和:
find . -exec sh -c "echo {}" \;
Run Code Online (Sandbox Code Playgroud)
到:
find . -exec sh -c 'echo "$1"' sh {} \;
Run Code Online (Sandbox Code Playgroud)
但这不适用于 shell -> sed 或 awk -> shell 案例。
当 1 不可能时,您需要清理变量以删除或转义可能有问题的字符。在,
awk '{system("echo foo: " $0)}'
Run Code Online (Sandbox Code Playgroud)
$0就外壳而言,您需要转换为干净的字符串。一种选择是用反斜杠作为每个字符的前缀,但这不适用于换行符(这里不是问题)。另一种方法是将字符串括在单引号中并转义每个单引号。
awk 'function escape(s) {
gsub(/'\''/,"&\\\\&&",s)
return "'\''" s "'\''"
}
{system("echo foo: " escape($0))}'
Run Code Online (Sandbox Code Playgroud)这两个示例说明了差异:
$ echo _TMP_ | awk -v VAR='some "text"' '{ gsub(/_TMP_/, VAR) ; print }'
some "text"
$ echo _TMP_ | awk -v VAR='some "text"' '{ gsub(/_TMP_/, "VAR") ; print }'
VAR
Run Code Online (Sandbox Code Playgroud)
当VAR不加引号时,awk将其视为值为 的变量some "text"。当VAR是在引号内时,awk 将其视为一个三字符的字符串。
更多: bash有消毒问题。考虑:
$ VAR="rm important_file" ; $VAR
Run Code Online (Sandbox Code Playgroud)
以上将擦除important_file。这样, bash就像一个宏语言:它会替换一个变量,然后尝试执行结果。awk是不同的。考虑:
$ echo _TMP_ | awk -v VAR='var); print $1' '{ gsub(/_TMP_/, VAR) ; print }'
var); print $1
Run Code Online (Sandbox Code Playgroud)
awk将其视为VAR纯文本,而不是要执行的潜在命令。
然而,如果我们bash修改awk脚本,就会出现问题。在我上面的例子中,awk脚本都是用单引号引起来的。这可以防止bash弄乱他们。