我是否需要将 awk 变量封装在引号中才能对其进行清理?

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)

或者这没有区别?

Sté*_*las 5

(好吧,抱歉我读得太快了,所以我的一些回答有点离题,仍然保持原样,因为它可能对您或某些人有用)

这里有几件事情需要考虑。

引用shell变量

在 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而不进行扩展,您可以使用ENVIRONARGVawk 数组:

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)

现在,为了避免这些问题,有两种通用方法:

  1. 变量从一个解释器转换为另一个解释器。这适用于 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 案例。

  2. 当 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)


Joh*_*024 5

这两个示例说明了差异:

$ 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弄乱他们。