Muss ich awk-Variablen in Anführungszeichen kapseln, um sie zu bereinigen?

7

Nach einer Antwort zum Stackoverflow ist das Einkapseln von Bash-Variablen in doppelte Anführungszeichen meines Erachtens eine ziemlich sichere Methode, um Benutzereingaben zu bereinigen.

Was ist mit awk-Variablen? Zum Beispiel, wenn ich so etwas habe wie:

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

Sollte ich die Variable in den gsub-Zeilen in Anführungszeichen setzen? So würde es dann aussehen:

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

Oder macht das keinen Unterschied?

Mike B.
quelle
3
Nein, wenn Sie SOURCEIP in awk in Anführungszeichen setzen, wird es nicht als Variable verarbeitet.
Eile

Antworten:

5

Diese beiden Beispiele zeigen den Unterschied:

$ 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

Wenn VARnicht in Anführungszeichen gesetzt ist, wird awkes als Variable mit dem Wert behandelt some "text". Wenn VARes in Anführungszeichen steht, behandelt awk es als dreistellige Zeichenfolge.

MEHR: bash hat Desinfektionsprobleme. Erwägen:

$ VAR="rm important_file" ; $VAR

Das obige wird gelöscht important_file. Auf diese Weise bashist es wie eine Makrosprache: Sie ersetzt eine Variable und versucht dann, das Ergebnis auszuführen. awkist anders. Erwägen:

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

awkbehandelt VARwie bloßer Text, nicht wie mögliche auszuführende Befehle.

Probleme können jedoch auftreten, wenn man bashdas awkSkript ändern lässt . In meinen obigen Beispielen waren die awkSkripte alle in einfachen Anführungszeichen. Das verhindert, dass man bashsich mit ihnen anlegen kann.

John1024
quelle
1
VAR='blah; echo $1'ist auch für die Shell kein Problem (es sei denn, Sie verwenden eval). Es ist keine Makrosprache (außer bis zu einem gewissen Grad alias Erweiterung)
Stéphane Chazelas
4

(OK, tut mir leid, ich habe Ihre Frage zu schnell gelesen, daher ist ein Teil meiner Antwort etwas nebensächlich und lässt sie immer noch so, wie sie für Sie oder andere nützlich sein kann.)

Hier sind einige Dinge zu beachten.

Zitieren von Shell- Variablen

Wenn Sie eine Variable in POSIX-Shells nicht in Anführungszeichen setzen (in Listenkontexten, wie in Argumenten für einen Befehl), ist dies nicht awkder Operator split + glob.

Wenn Sie tun:

cmd foo=$var

Wo $varist * *.

Bitten Sie die Shell nicht, den Inhalt $varbasierend auf dem Wert der $IFSspeziellen Shell-Variablen zu teilen , standardmäßig auf Leerzeichen. Das gibt uns also foo=*und *und führt Globbing für jeden von diesen durch, dh erweitert foo=*auf alle Dateinamen im aktuellen Verzeichnis, die mit foo=und *auf alle nicht versteckten Dateinamen beginnen.

Sie sollten also fast immer Ihre Shell- Variablen zitieren , unabhängig davon, ob es sich um Argumente handelt awkoder nicht. Dies gilt auch für die Shell-Befehlsersetzung ( `...`und $(...)) und die Shell-Arithmetik-Erweiterung ( $((...))).

Daten so wie sie sind an übergeben awk

Das andere Problem ist, dass awk(nicht die Shell) Backslash-Escape-Sequenzen in den Zuweisungen von Variablen wie -v var=value (und mit GNU awk4.2 oder höher, wenn der Wert mit beginnt @/und endet /, als regulärer Variablentyp behandelt wird ) erweitert wird.

Setzt beispielsweise -v var='\n/\n/'den Inhalt der awk varVariablen auf <newline>/<newline>/nicht \n/\n/. Dies gilt auch für awkVariablen, die definiert sind als:

awk '...' var=value

Um Daten zu übergeben, awkohne dass diese Erweiterung durchgeführt wird, können Sie die Arrays ENVIRONoder ARGVawk verwenden:

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

(oben ist es eine Shell-Variablenzuweisung (zu einer Nicht-Array-Variablen), daher kann es keine Aufteilung + Glob geben, was einer der seltenen Fälle ist, in denen Sie die Anführungszeichen um Variablen weglassen können.)

oder:

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

Anführungszeichen und awkVariablen

Dieser Split + Glob ist nur eine Shell- (Fehl-) Funktion. Die awkSprache ist eine ganz andere Sprache.

In awkbeziehen sich Variablen auf a varname, not $varnameund Anführungszeichen werden verwendet, um Zeichenfolgen einzuführen. So "varname"ist die varnameZeichenfolge, während varnameauf die Variable verweist.

Bereinigen von Variablen, um Code-Injection zu vermeiden

Genau genommen ist das Zitieren von Shell-Variablen keine Bereinigung, es zitiert nicht die Variablen , die den Operator split + glob verwenden. Während Sie in den meisten Sprachen Anführungszeichen um feste Zeichenfolgen setzen, ist es in Shells umgekehrt: Alles ist Zeichenfolge, und Anführungszeichen werden verwendet, um ein bestimmtes Verhalten zu verhindern, und insbesondere Variablen sollten fast immer in Anführungszeichen gesetzt werden (eine schlechte Entwurfsentscheidung dieser Art) machte in der Bourne-Muschel in den 70er Jahren Sinn, ist aber ein Hindernis für moderne Muscheln, da zshes die einzige Muschel ist, die dies teilweise behoben hat.

Die Shell oder awk wertet / interpretiert keinen in ihrer eigenen Variablen gespeicherten Code aus, es sei denn, Sie weisen sie an.

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

Bewirkt nicht, dass der Inhalt der Variablen als Shell-Code ausgewertet wird (obwohl der erste Code aufgeteilt und globalisiert wird, was schwerwiegende Folgen haben kann (z. B. mit var='/*/*/*/*/../../../../*/*/*/*/../../../../*/*/*/*'). Sie benötigen:

eval "echo $var"
# or
sh -c "echo $var"

damit es als Shell-Code ausgewertet / interpretiert wird.

awkhat keine solche evalFunktion. perlIch pythonmache.

Achten Sie jedoch auf Kreuzkontaminationen. Sie können die Shell-Variablendaten (in Shell- Variablen) als Code ausführen lassen, um Folgendes auszuführen awk:

awk '{print "'"$var"': " $0}'

wäre gefährlich, wenn die $var Shell- Variable zum Beispiel enthält:

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

weil die Shell dann ausführen würde:

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

Oder umgekehrt:

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

Wo awkwürde eine Shell laufen als:

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

für jede Zeile von file(und überlegen Sie, was eine Zeile wie ; rm -rf /tun würde).

Es ist nicht nur zwischen awkund sh. Sie müssen vorsichtig sein, wenn variable / unkontrollierte Daten von einem anderen Interpreter als Code ausgewertet werden. Beispiele sind:

sed "s/$regexp/blah/g"

sed's Sprache ist begrenzt, aber es kann immer noch schaden, wie bei regexp='//;w /etc/passwd; s/'.

Oder:

find . -exec sh -c "echo {}" \;

Um diese Probleme zu vermeiden, gibt es zwei allgemeine Ansätze:

  1. Konvertieren Sie die Variable von einem Interpreter in den anderen. Das funktioniert für die Shell -> awk oder find -> sh case oben. Wie Veränderung:

    awk '{print "'"$var"': " $0}'

    zu:

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

    Und:

    find . -exec sh -c "echo {}" \;

    zu:

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

    Aber das funktioniert nicht für die Shell -> sed oder awk -> Shell Fälle.

  2. Wenn 1 nicht möglich ist, müssen Sie die Variablen bereinigen, um die möglicherweise problematischen Zeichen zu entfernen oder zu entfernen. Im,

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

    Sie müssen in $0etwas konvertieren , das für die Shell eine saubere Zeichenfolge ist. Eine Möglichkeit besteht darin, jedem Zeichen einen Backslash voranzustellen, dies funktioniert jedoch nicht für Zeilenumbrüche (hier kein Problem). Eine andere Möglichkeit besteht darin, die Zeichenfolge in einfache Anführungszeichen zu setzen und jedes einzelne Anführungszeichen zu umgehen.

    awk 'function escape(s) {
           gsub(/'\''/,"&\\\\&&",s)
           return "'\''" s "'\''"
         }
         {system("echo foo: " escape($0))}'
    
Stéphane Chazelas
quelle
Danke, das sind großartige Informationen. Ich bin immer noch ein wenig verwirrt über die "Sicherheit", solche Sachen herumzugeben. In meinem Fall möchte ich, dass es sich ausdehnt, aber ich möchte nicht, dass es Chaos anrichtet. Nehmen wir zum Zwecke der Diskussion an, dass der Wert der Shell-Variablen $SOURCEIPist rm -fr /. Wenn ich das an awk via weitergebe awk -v AWKVAREXAMPLE="$SOURCEIP"und später awk einen gsub machen lasse, gsub(/^_TARGETSTRING_/, AWKVAREXAMPLE);würde das irgendwann in die Shell "lecken" und alles zerstören?
Mike B
1
@ MikeB, nein. Es würde an die Shell auslaufen, wenn awkeine Shell aufgerufen und als Code übergeben würde, den sie wie folgt interpretieren könnte: awk '{system("echo " var)}'(wo varist ;rm -rf /), wo awkruft auf ["sh", "-c", "echo; rm -rf /"]oder awk '{print | "tr " v1 " " v2}'wohin awkleitet die Ausgabe weiter ["sh", "-c", "tr content-of-v1 content-of-v2"].
Stéphane Chazelas
1
Dinge, die Sie vermeiden möchten, sind: awk "{print \"$shell_variables\"}"Wie dort wird der Inhalt der Shell-Variablen als awk-Code interpretiert.
Stéphane Chazelas
0

Wenn Sie eine Awk-Variable an das System übergeben , müssen Sie sie in Anführungszeichen setzen:

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
}

Beispiel:

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

Quelle

Steven Penny
quelle