Ist es gefährlich, Echo ohne Anführungszeichen auszuführen?

11

Ich habe einige ähnliche Themen gesehen, aber sie beziehen sich darauf, keine Variablen zu zitieren, von denen ich weiß, dass sie zu unerwünschten Ergebnissen führen können.

Ich habe diesen Code gesehen und mich gefragt, ob es möglich ist, etwas einzufügen, das ausgeführt werden soll, wenn diese Codezeile ausgeführt wird:

echo run after_bundle

Viktor Fonic
quelle
Ich bin darauf gestoßen, als ich hatte: target = "*** LIVE SERVER ***"; Echo-Ziel: $ Ziel; und der *** wurde zu einem Ordner erweitert ... 😬
Matt Parkins

Antworten:

17

Für den speziellen Fall

echo run after_bundle

Zitate sind nicht erforderlich. Es ist kein Anführungszeichen erforderlich, da das Argument echostatische Zeichenfolgen sind, die keine Variablenerweiterungen oder Befehlsersetzungen usw. enthalten. Es handelt sich um "nur zwei Wörter" (und wie Stéphane betont , werden sie zusätzlich aus dem tragbaren Zeichensatz erstellt ).

Die "Gefahr" entsteht, wenn Sie mit variablen Daten arbeiten, die die Shell erweitern oder interpretieren kann. In solchen Fällen muss darauf geachtet werden, dass die Shell das Richtige tut und das Ergebnis dem entspricht, was beabsichtigt ist.

Die folgenden zwei Fragen enthalten relevante Informationen dazu:


echowird manchmal verwendet, um potenziell schädliche Befehle in Antworten auf dieser Site zu "schützen". Zum Beispiel kann ich zeigen, wie Dateien mit entfernt oder an ein neues Ziel verschoben werden

echo rm "${name##*/}.txt"

oder

echo mv "$name" "/new_dir/$newname"

Dies würde Befehle auf dem Terminal ausgeben, anstatt Dateien tatsächlich zu entfernen oder umzubenennen. Der Benutzer kann dann die Befehle überprüfen, entscheiden, dass sie in Ordnung aussehen, sie entfernen echound erneut ausführen.

Ihr Befehl echo run after_bundlekann eine Anweisung an den Benutzer sein, oder es kann sich um einen "auskommentierten" Code handeln, der zu gefährlich ist, um ausgeführt zu werden, ohne die Konsequenzen zu kennen.

Mit echowie dieser, muss man wissen , was die modifizierte Befehl tut und man muss sicherstellen , dass der modifizierte Befehl tatsächlich ist sicher (es wäre möglicherweise nicht , wenn es Umleitungen enthalten ist , und es auf einer Pipeline mit nicht funktioniert, etc.)

Kusalananda
quelle
Das Hinzufügen von Anführungszeichen reicht jedoch nicht aus, um zu wissen, was eine Shell tun würde - genau wie Sie nicht sagen können, dass dies echo rm "first file.txt" "second file.txt"in irgendeiner Weise anders ist echo rm "first" "file.txt" "second" "file.txt", da die Ausgabe von beiden gleich ist. Wenn Sie einen Shell-Befehl als Ausgabe generieren möchten, müssen Sie einen printf '%q ' rm "first file.txt" "second file.txt"; echooder einen entsprechenden Befehl verwenden , der syntaktische Anführungszeichen neu generiert, die als übergeben ausgewertet werden argv.
Charles Duffy
@CharlesDuffy Ich hoffe wirklich, dass niemand die Debugging-Ausgabe kopiert und einfügt und sie in der Shell ausführt !
Kusalananda
1
Das Generieren von Shell-Befehlen und das anschließende Weiterleiten an Shell-Befehle shist kein ungewöhnliches Muster. Die Leute fragen: "Warum funktioniert das foo, wenn ich es in einer Befehlszeile ausführe, aber dieses Skript, das genau diese Zeichenfolge mit echovor der Zeile ausgibt, tut dies nicht." "" passiert die ganze Zeit hier. Das Debuggen der Ausgabe ist nicht hilfreich, wenn es Ihre Fehler verbirgt - und wenn Ihre Fehler mit dem Zitieren zusammenhängen, echowerden sie nicht angezeigt.
Charles Duffy
27

Nur eine zusätzliche Anmerkung zu @ Kusalanandas guter Antwort .

echo run after_bundle

ist in Ordnung, da keines der Zeichen in diesen 3 Argumenten¹ an übergeben wurde echo Zeichen enthält, die für die Shell speziell sind.

Und (der zusätzliche Punkt, den ich hier ansprechen möchte) es gibt kein Systemgebietsschema, in dem diese Bytes in Zeichen übersetzt werden könnten, die für die Shell speziell sind.

Alle diese Zeichen befinden sich in dem, was POSIX als tragbaren Zeichensatz bezeichnet . Diese Zeichen sollten in allen Zeichensätzen auf einem POSIX-System² vorhanden und gleich codiert sein.

Diese Befehlszeile wird also unabhängig vom Gebietsschema gleich interpretiert.

Wenn wir jetzt Zeichen außerhalb dieses tragbaren Zeichensatzes verwenden, ist es eine gute Idee, sie in Anführungszeichen zu setzen, auch wenn sie nicht speziell für die Shell sind, da in einem anderen Gebietsschema die Bytes, aus denen sie bestehen, möglicherweise als unterschiedliche Zeichen interpretiert werden speziell für die Shell. Beachten Sie echo, dass das Problem nicht bei einem Befehl oder einem anderen Befehl liegtecho sondern darin, wie die Shell ihren Code analysiert.

Zum Beispiel in einem UTF-8:

echo voilà | iconv -f UTF-8 -t //TRANSLIT

Das àist als 0xc3 0xa0 codiert. Wenn Sie diese Codezeile in einem Shell-Skript haben und das Shell-Skript von einem Benutzer aufgerufen wird, der ein Gebietsschema verwendet, dessen Zeichensatz nicht UTF-8 ist, können diese beiden Bytes sehr unterschiedliche Zeichen enthalten.

In einem fr_FR.ISO8859-15Gebietsschema, einem typischen französischen Gebietsschema, das den Standard-Einzelbyte-Zeichensatz verwendet, der die französische Sprache abdeckt (dasselbe gilt für die meisten westeuropäischen Sprachen einschließlich Englisch), wird dieses 0xc3-Byte als das interpretiertà Zeichen und 0xa0 als Nicht- Breaking Space Charakter.

Und auf einigen Systemen wie NetBSD³ wird dieses nicht unterbrechende Leerzeichen als leeres Zeichen betrachtet ( isblank()auf dem true zurückgegeben wird, mit dem es übereinstimmt [[:blank:]]), und Shells wie bashbehandeln es daher als Token-Trennzeichen in ihrer Syntax.

Das bedeutet , dass anstelle des Laufens echomit $'voil\xc3\xa0'als Argument, laufen sie es mit $'voil\xc3'als Argument, was bedeutet , es nicht gedruckt wird , voilàkorrekt.

Es wird noch viel schlimmer mit chinesischen Zeichensätzen wie BIG5, BIG5-HKSCS, GB18030, GBK , die viele Zeichen , deren Codierung enthält die gleiche Codierung wie die haben |, `, \(zu nennen das Schlimmste) (auch , dass lächerliche SJIS, auch bekannt als Microsoft Kanji, mit Ausnahme dass es ¥statt \, aber immer noch als behandelt wird\ von den meisten Tools wird, da es dort als 0x5c codiert ist).

Wenn Sie sich beispielsweise in einem zh_CN.gb18030chinesischen Gebietsschema befinden, schreiben Sie ein Skript wie:

echo  reboot

Dieses Skript wird 詜 rebootin einem Gebietsschema mit GB18030 oder GBK, 唰 rebootin einem Gebietsschema mit BIG5 oder BIG5-HKSCS, aber in einem C-Gebietsschema mit ASCII oder einem Gebietsschema mit ISO8859-15 oder UTF-8 ausgegeben, rebootda die GB18030-Codierung ausgeführt wird von ist 0xd4 0x7c und 0x7c ist die Codierung von |in ASCII, so dass wir am Ende laufen:

 echo �| reboot

(das , das jedoch das 0xd4-Byte darstellt, wird im Gebietsschema gerendert). Beispiel mit dem weniger schädlichen unameanstelle von reboot:

$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$

( unamewurde ausgeführt).

Mein Rat wäre also, alle Zeichenfolgen zu zitieren, die Zeichen außerhalb des tragbaren Zeichensatzes enthalten.

Beachten Sie jedoch, dass es besser ist, nicht oder oder (innerhalb dessen und / oder immer noch speziell) zu verwenden, sondern stattdessen Zeichen außerhalb des tragbaren Zeichensatzes zu zitieren , da die Codierung von \und `in der Codierung einiger dieser Zeichen enthalten ist.\"..."$'...'`\'...'

Ich bin mir nicht bewusst jedes System , das eine locale hat , wo die charset jedes Zeichen hat (außer 'sich selbst natürlich) , dessen Codierung enthält die Kodierung ', so dass diejenigen , auf '...'jeden Fall die sicherste sein sollte.

Beachten Sie, dass mehrere Shells auch eine $'\uXXXX'Notation unterstützen, um Zeichen basierend auf ihrem Unicode-Codepunkt auszudrücken. In Shells wie zshund bashwird das Zeichen codiert in den Zeichensatz des Gebietsschemas eingefügt (kann jedoch zu unerwartetem Verhalten führen, wenn dieser Zeichensatz dieses Zeichen nicht enthält). Auf diese Weise können Sie vermeiden, Nicht-ASCII-Zeichen in Ihren Shell-Code einzufügen.

Also oben:

echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'

Oder:

echo $'voil\u00e0'
echo $'\u8a5c reboot'

(Mit der Einschränkung könnte das Skript beschädigt werden, wenn es in Gebietsschemas ausgeführt wird, in denen diese Zeichen nicht vorhanden sind.)

Oder besser, da dies \auch etwas Besonderes ist echo(oder zumindest einige echo Implementierungen, zumindest die Unix-kompatiblen):

printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'

(Beachten Sie, dass dies \auch im ersten Argument für etwas Besonderes ist printf, sodass Nicht-ASCII-Zeichen dort auch besser vermieden werden, falls sie die Codierung von enthalten können \.)

Beachten Sie, dass Sie auch Folgendes tun können:

'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'

(Das wäre übertrieben, könnte Ihnen aber Sicherheit geben, wenn Sie nicht sicher sind, welche Zeichen im tragbaren Zeichensatz enthalten sind.)

Stellen Sie außerdem sicher, dass Sie niemals die alte `...`Form der Befehlssubstitution verwenden (die eine andere Ebene der Backslash-Verarbeitung einführt), sondern $(...)stattdessen verwenden.


¹ wird technisch echoauch als Argument an das echoDienstprogramm übergeben (um zu sagen, wie es aufgerufen wurde), es ist das argv[0]und argcist 3, obwohl in den meisten Shells heutzutage echoeingebaut ist, so dass das exec()einer /bin/echoDatei mit einer Liste von 3 Argumenten durch das simuliert wird Schale. Es ist auch üblich, die Liste der Argumente so zu betrachten, dass sie mit dem zweiten ( argv[1]bis argv[argc - 1]) beginnt , da dies die sind, auf die die Befehle hauptsächlich einwirken.

² eine bemerkenswerte Ausnahme davon ist das lächerliche ja_JP.SJISGebietsschema von FreeBSD-Systemen, deren Zeichensatz weder \noch ~Charakter hat!

³ Beachten Sie, dass viele Systeme (FreeBSD, Solaris, jedoch keine GNU-Systeme) U + 00A0 als [[:blank:]]in UTF-8-Gebietsschemas betrachten, aber nur wenige in anderen Gebietsschemas wie denen, die ISO8859-15 verwenden, möglicherweise, um diese Art von Problem zu vermeiden.

Stéphane Chazelas
quelle
In Ihrem ersten Absatz sagen Sie uns "... von den Zeichen in diesen 3 Argumenten, die an echo... übergeben wurden", ich zähle nur 2 Argumente, die an den Befehl übergeben werden echo, die Argumente, die ich zählen kann, sind runund after_bundleerklären, wie Sie gezählt und zu 3 Argumenten gekommen?
Ferrybig
1
@ViktorFonic, siehe Bearbeiten über die Anzahl der Argumente (und dass das Hauptproblem nicht mit ist echo). (exec -a foo /bin/echo --help)Informationen zum Übergeben eines beliebigen ersten Arguments an das /bin/echoDienstprogramm finden Sie auf einem GNU-System und mit der GNU-Shell .
Stéphane Chazelas
@Ferrybig Siehe Stephanes Bearbeitung, Fußnote 1. Argumente, die im üblichen C-Stil befohlen werden sollen, sind eine Reihe von Argumenten, wobei argv [0] der ausführbare Name selbst ist. Ein bisschen ähnlich $0und Positionsparameter in Schalen.
Sergiy Kolodyazhnyy
Es gibt 373 Codierungen, iconvin die ESCkonvertiert wird '. Versuchen Sie (als Beispiel):printf '\x1b'|iconv -f utf8 -t IBM-937|xxd
Isaac
Es gibt 173 Codierungen, bei denen ein Codepunkt (außer ESC) in a konvertiert wird '. Versuchen Sie es printf '\u2804' | iconv -f utf8 -t BRF | xxd. Es gibt Codierungen, in denen viele Codepunkte entstehen '. Rund 8695 Codepunkte in UCS-4 werden '. Versuchen Sie es printf '\U627' | iconv -cf utf-8 -t UCS-4. Mehrere (37) Codierungen konvertieren das Zeichen 0x127 in a '. Versuchen Sieprintf '\U127' | iconv -cf utf8 -t UCS2 |xxd
Isaac