web-dev-qa-db-ja.com

それらをサニタイズするために、awk変数を引用符でカプセル化する必要がありますか?

stackoverflowの回答 にあるように、bash変数を二重引用符でカプセル化することは、ユーザー入力を無害化するかなり安全な方法であると理解しています。

Awk変数はどうですか?たとえば、次のような場合:

awk -v SOURCEIP="$SOURCEIP" -v REVERSEDNS="$REVERSEDNS" '{
   gsub(/^_TMPSOURCEIP_/, SOURCEIP);
   gsub(/^_TMPREVERSEDNS_/, REVERSEDNS);
   print
}' /home/foo/footemplate

Gsub行の変数を引用符で囲む必要がありますか?したがって、次のようになります。

awk -v SOURCEIP="$SOURCEIP" -v REVERSEDNS="$REVERSEDNS" '{
   gsub(/^_TMPSOURCEIP_/, "SOURCEIP");
   gsub(/^_TMPREVERSEDNS_/, "REVERSEDNS");
   print
}' /home/foo/footemplate

それとも違いはありませんか?

7
Mike B

次の2つの例は、違いを示しています。

$ 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

VARが引用符で囲まれていない場合、awkは値some "text"を持つ変数として扱います。 VARが引用符の内側にある場合、awkはそれを3文字の文字列として扱います。

MORE:bashにはサニタイズの問題があります。検討してください:

$ VAR="rm important_file" ; $VAR

上記はimportant_fileを消去します。このように、bashはマクロ言語のようなものです。変数の代わりに使用して、結果を実行しようとします。 awkは異なります。検討してください:

$ echo _TMP_ | awk -v VAR='var); print $1' '{ gsub(/_TMP_/, VAR) ; print }'
var); print $1

awkは、実行する可能性のあるコマンドではなく、単なるテキストのようにVARを扱います。

ただし、bashawkスクリプトを変更させると、問題が発生する可能性があります。上記の例では、awkスクリプトはすべて単一引用符で囲みました。これはbashがそれらを乱用するのを防ぎます。

5
John1024

(申し訳ありませんが、あなたの質問をあまりにも早く読んだので、私の答えのいくつかは要点の少しずれていますが、それでもあなたや一部にとって役立つかもしれないので、そのままにしておきます)

ここで考慮すべき点がいくつかあります。

Shell変数の引用

awkではなく、POSIXシェルで変数を引用符で囲まずに(コマンドの引数のようにリストコンテキストで)残すのは、split + glob演算子です。

もし、するなら:

_cmd foo=$var
_

ここで、_$var_は_* *_です。

シェルに_$var_の特別なシェル変数の値に基づいて_$IFS_のコンテンツを分割するように要求するのではなく、デフォルトでは空白になっています。上記のように、これにより_foo=*_と_*_が得られ、それぞれに対してグロビングを実行します。つまり、_foo=*_を_foo=_で始まる現在のディレクトリ内のすべてのファイル名に展開します。 _*_を非表示ではないすべてのファイル名に。

したがって、実際には、Shell変数は、awkの引数であるかどうかにかかわらず、ほとんど常に引用する必要があります。これは、シェルコマンド置換(_`...`_および$(...))およびシェル算術展開($((...)))にも適用されます。

データをそのままawkに渡す

その他の問題は、awk(シェルではない)が_-v var=value_(およびGNU awk 4.2以降、 値が_@/_で始まり_/_で終わる場合、これは正規表現型の変数として扱われます )。

たとえば、_-v var='\n/\n/'_は、awkvar変数の内容を_<newline>/<newline>/_ではなく_\n/\n/_に設定します。これは、次のように定義されたawk変数にも適用されます。

_awk '...' var=value
_

展開せずにデータをawkに渡すには、ENVIRONまたはARGV awk配列を使用できます。

_var=$value awk 'BEGIN {var=ENVIRON["var"]} ...'
_

(上記は、(配列以外の変数への)シェル変数の割り当てであるため、split + globはできません。これは、変数を囲む引用符を省略できるまれなケースの1つです)

または:

_awk 'BEGIN {var=ARGV[1]; delete ARGV[1]} ...' "$value"
_

クォートとawk変数

そのsplit + globはシェル(誤)機能にすぎません。 awk言語は完全に異なる言語です。

awkでは、変数は_$varname_ではなくvarnameを参照し、引用符を使用して文字列を紹介します。したがって、_"varname"_はvarname文字列であり、varnameは変数を参照します。

コードインジェクションを回避するための変数のサニタイズ

厳密に言うと、Shell変数の引用は無害化ではなく、split + glob演算子を使用している変数を引用しないです。ほとんどの言語では固定文字列を引用符で囲みますが、シェルではそれが逆になります。すべてのものは文字列であり、引用符は特別な動作を防ぐために使用されます。特に変数はほとんど常に引用符で囲まれる必要があります70年代のBourneシェルでは理にかなっていますが、現代のシェルでは邪魔です。zshはそれを部分的に修正した唯一のシェルです)。

シェルまたはawkは、ユーザーが指示しない限り、独自の変数に格納されているコードを評価または解釈しません。

_var='foo; rm -f var'
echo $var
# or
echo "$var"
_

変数の内容がシェルコードとして評価されることはありません(ただし、最初のコードはスプリットとグロビングを実行するため、悲惨な結果を招く可能性があります(たとえば、_var='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*'_を使用)。次のものが必要です。

_eval "echo $var"
# or
sh -c "echo $var"
_

シェルコードとして評価/解釈されるため。

awkには、そのようなeval機能はありません。 Perl/pythonします。

ただし、相互汚染に注意してください。 awkで実行するコードとして、シェルに変数データ(Shell変数内)を渡すことができます。

_awk '{print "'"$var"': " $0}'
_

_$var_ Shell変数に次の例が含まれている場合は危険です:

_var='test"; print "foo" > /etc/passwd; print "blah'
_

シェルが実行するため:

_["awk", "{print \"test\"; print \"foo\" > /etc/passwd; print \"blah: \" $0}"]
_

またはその逆:

_awk '{system("echo foo: " $0)}' < file
_

ここで、awkは次のようにシェルを実行します。

_["sh", "-c", "echo foo: content-of-the-line"]
_

fileの各行について(そして_; rm -rf /_のような行が何をするかを考えてください)。

awkshの間だけではありません。変数/制御されていないデータが別のインタープリターによってコードとして評価される可能性がある場合は常に注意する必要があります。次に例を示します。

_sed "s/$regexp/blah/g"
_

sedの言語は限られていますが、_regexp='//;w /etc/passwd; s/_ 'のように、まだ害を及ぼす可能性があります。

または:

_find . -exec sh -c "echo {}" \;
_

これらの問題を回避するために、2つの一般的なアプローチがあります。

  1. 変数を1つのインタープリターから別のインタープリターに変換します。これは、上記のシェル-> awkまたはfind-> shの場合に機能します。変化のように:

    _awk '{print "'"$var"': " $0}'
    _

    に:

    _awk -v awk_var="$var" '{print awk_var ": " $0}'
    _

    そして:

    _find . -exec sh -c "echo {}" \;
    _

    に:

    _find . -exec sh -c 'echo "$1"' sh {} \;
    _

    しかし、シェル-> sed、またはawk->シェルの場合は機能しません。

  2. 1が不可能な場合は、変数をサニタイズして、問題となる可能性のある文字を削除またはエスケープする必要があります。に、

    _awk '{system("echo foo: " $0)}'
    _

    シェルに関する限り、_$0_をクリーンな文字列に変換する必要があります。 1つのオプションは、各文字の前にバックスラッシュを付けることですが、それは改行では機能しません(ここでは問題ではありません)。もう1つは、文字列を単一引用符で囲み、各単一引用符をエスケープすることです。

    _awk 'function escape(s) {
           gsub(/'\''/,"&\\\\&&",s)
           return "'\''" s "'\''"
         }
         {system("echo foo: " escape($0))}'
    _
4

Awk変数をsystemに渡す場合は、シェルで引用する必要があります。

function quote(str,   d, m, x, y, z) {
  d = "\47"; m = split(str, x, d)
  for (y in x) z = z d x[y] d (y < m ? "\\" d : "")
  return z
}

例:

system(sprintf("ffmpeg -i %s outfile.m4a", quote(ARGV[1])))

ソース

0
Steven Penny