Welche Zeichen müssen bei der Verwendung von Bash maskiert werden?

206

Gibt es eine umfassende Liste von Zeichen, die in Bash maskiert werden müssen? Kann es nur mit überprüft werden sed?

Insbesondere habe ich geprüft, ob %entkommen muss oder nicht. Ich habe es versucht

echo "h%h" | sed 's/%/i/g'

und funktionierte gut, ohne zu entkommen %. Bedeutet das, %dass man nicht entkommen muss? War dies ein guter Weg, um die Notwendigkeit zu überprüfen?

Und allgemeiner: sie sind die gleichen Zeichen in zu entkommen shellund bash?

fedorqui 'SO hör auf zu schaden'
quelle
4
Wenn es Sie interessiert, machen Sie es im Allgemeinen falsch. Der Umgang mit Daten sollte niemals das Durchlaufen des für Code verwendeten Analyse- und Auswertungsprozesses beinhalten, wodurch das Entkommen in Frage gestellt wird. Dies ist eine sehr enge Parallele zu Best Practices für SQL - wobei das Richtige darin besteht, Bindungsvariablen zu verwenden, und das Falsche darin besteht, zu versuchen, Daten zu "bereinigen", die über Zeichenfolgenersetzungen eingefügt wurden.
Charles Duffy
Im Zusammenhang mit stackoverflow.com/questions/2854655/…
skywinder
8
@CharlesDuffy Ja, aber manchmal entgeht das, was die Engine für vorbereitete Anweisungen im Backend tut, nur den Dingen. Ist SO "falsch", weil sie von Benutzern eingereichten Kommentaren entgehen, bevor sie im Browser angezeigt werden? Nein, sie verhindern XSS. Sich überhaupt nicht darum zu kümmern, macht es falsch.
Parthian Shot
@ParthianShot, wenn die vorbereitete Anweisungs-Engine die Daten nicht vollständig außerhalb des Bandes vom Code hält, sollten die Personen, die sie geschrieben haben, erschossen werden. Ja, ich weiß, dass das Wire-Protokoll von MySQL auf diese Weise implementiert ist. Meine Aussage steht.
Charles Duffy
@CharlesDuffy Und mein Punkt - manchmal besteht Ihre Möglichkeit darin, etwas sicher zu machen, indem Sie eine Toolchain verwenden, die einen Puristen erschüttert, oder achtmal so viel Zeit und Mühe zu investieren, um es hübsch zu machen - steht auch noch.
Parthian Shot

Antworten:

282

Es gibt zwei einfache und sichere Regeln, die nicht nur funktionieren, shsondern auch bash.

1. Setzen Sie die gesamte Zeichenfolge in einfache Anführungszeichen

Dies funktioniert für alle Zeichen außer Anführungszeichen. Um dem einfachen Anführungszeichen zu entkommen, schließen Sie das Angebot davor, fügen Sie das einfache Anführungszeichen ein und öffnen Sie das Angebot erneut.

'I'\''m a s@fe $tring which ends in newline
'

sed Befehl: sed -e "s/'/'\\\\''/g; 1s/^/'/; \$s/\$/'/"

2. Entkomme jedem Zeichen mit einem Backslash

Dies funktioniert für alle Zeichen außer Zeilenumbruch. Verwenden Sie für Zeilenumbrüche einfache oder doppelte Anführungszeichen. Leere Zeichenfolgen müssen noch behandelt werden - ersetzen durch""

\I\'\m\ \a\ \s\@\f\e\ \$\t\r\i\n\g\ \w\h\i\c\h\ \e\n\d\s\ \i\n\ \n\e\w\l\i\n\e"
"

sed Befehl : sed -e 's/./\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.

2b. Lesbarere Version von 2

Es gibt eine einfache und sichere Reihe von Zeichen, [a-zA-Z0-9,._+:@%/-]die nicht entfernt werden können, um die Lesbarkeit zu verbessern

I\'m\ a\ s@fe\ \$tring\ which\ ends\ in\ newline"
"

sed Befehl : LC_ALL=C sed -e 's/[^a-zA-Z0-9,._+@%/-]/\\&/g; 1{$s/^$/""/}; 1!s/^/"/; $!s/$/"/'.


Beachten Sie, dass in einem sed-Programm nicht bekannt ist, ob die letzte Eingabezeile mit einem Zeilenumbruchbyte endet (außer wenn es leer ist). Aus diesem Grund gehen beide oben genannten sed-Befehle davon aus, dass dies nicht der Fall ist. Sie können eine zitierte neue Zeile manuell hinzufügen.

Beachten Sie, dass Shell-Variablen nur für Text im POSIX-Sinne definiert sind. Die Verarbeitung von Binärdaten ist nicht definiert. Für die Implementierungen, die wichtig sind, funktioniert Binär mit Ausnahme von NUL-Bytes (da Variablen mit C-Zeichenfolgen implementiert sind und als C-Zeichenfolgen, nämlich Programmargumente, verwendet werden sollen). Sie sollten jedoch zu einem "binären" Gebietsschema wie latin1 wechseln .


(Sie können die Regeln einfach shüberprüfen, indem Sie die POSIX-Spezifikation für lesen . Informationen zu Bash finden Sie im Referenzhandbuch, das mit @AustinPhillips verknüpft ist.)

Jo So.
quelle
Hinweis: Eine gute Variation von # 1 ist hier zu sehen: github.com/scop/bash-completion/blob/… . Es muss nicht ausgeführt werden sed, erfordert jedoch bash.
JWD
4
Hinweis für alle anderen (wie mich!), Die Schwierigkeiten haben, diese zum Laufen zu bringen. Es sieht so aus, als würde der Sed-Geschmack, den Sie unter OSX erhalten, diese sed-Befehle nicht richtig ausführen. Sie funktionieren aber gut unter Linux!
Dalelane
@ Dalelane: Kann hier nicht testen. Bitte bearbeiten Sie, wenn Sie eine Version haben, die auf beiden funktioniert.
Jo So
Scheint, als hätten Sie etwas verpasst, sollte die Zeichenfolge mit einem '-' (Minus) beginnen, oder gilt das nur für Dateinamen? - Im letzteren Fall brauchen Sie ein './' vor.
Slashmais
Ich bin mir nicht sicher was du meinst. Mit diesen sed-Befehlen wird die Eingabezeichenfolge von stdin übernommen.
Jo So
59

Format, das als Shell-Eingabe wiederverwendet werden kann

Für diese Art von Anfrage gibt es eine spezielle printf Format-Direktive ( %q):

printf [-v var] Format [Argumente]

 %q     causes printf to output the corresponding argument
        in a format that can be reused as shell input.

Einige Beispiele:

read foo
Hello world
printf "%q\n" "$foo"
Hello\ world

printf "%q\n" $'Hello world!\n'
$'Hello world!\n'

Dies könnte auch durch Variablen verwendet werden:

printf -v var "%q" "$foo
"
echo "$var"
$'Hello world\n'

Schnelle Überprüfung mit allen (128) ASCII-Bytes:

Beachten Sie, dass alle Bytes von 128 bis 255 maskiert werden müssen.

for i in {0..127} ;do
    printf -v var \\%o $i
    printf -v var $var
    printf -v res "%q" "$var"
    esc=E
    [ "$var" = "$res" ] && esc=-
    printf "%02X %s %-7s\n" $i $esc "$res"
done |
    column

Dies muss so etwas wie Folgendes rendern:

00 E ''         1A E $'\032'    34 - 4          4E - N          68 - h      
01 E $'\001'    1B E $'\E'      35 - 5          4F - O          69 - i      
02 E $'\002'    1C E $'\034'    36 - 6          50 - P          6A - j      
03 E $'\003'    1D E $'\035'    37 - 7          51 - Q          6B - k      
04 E $'\004'    1E E $'\036'    38 - 8          52 - R          6C - l      
05 E $'\005'    1F E $'\037'    39 - 9          53 - S          6D - m      
06 E $'\006'    20 E \          3A - :          54 - T          6E - n      
07 E $'\a'      21 E \!         3B E \;         55 - U          6F - o      
08 E $'\b'      22 E \"         3C E \<         56 - V          70 - p      
09 E $'\t'      23 E \#         3D - =          57 - W          71 - q      
0A E $'\n'      24 E \$         3E E \>         58 - X          72 - r      
0B E $'\v'      25 - %          3F E \?         59 - Y          73 - s      
0C E $'\f'      26 E \&         40 - @          5A - Z          74 - t      
0D E $'\r'      27 E \'         41 - A          5B E \[         75 - u      
0E E $'\016'    28 E \(         42 - B          5C E \\         76 - v      
0F E $'\017'    29 E \)         43 - C          5D E \]         77 - w      
10 E $'\020'    2A E \*         44 - D          5E E \^         78 - x      
11 E $'\021'    2B - +          45 - E          5F - _          79 - y      
12 E $'\022'    2C E \,         46 - F          60 E \`         7A - z      
13 E $'\023'    2D - -          47 - G          61 - a          7B E \{     
14 E $'\024'    2E - .          48 - H          62 - b          7C E \|     
15 E $'\025'    2F - /          49 - I          63 - c          7D E \}     
16 E $'\026'    30 - 0          4A - J          64 - d          7E E \~     
17 E $'\027'    31 - 1          4B - K          65 - e          7F E $'\177'
18 E $'\030'    32 - 2          4C - L          66 - f      
19 E $'\031'    33 - 3          4D - M          67 - g      

Wenn das erste Feld der Hexa-Wert des Bytes ist, enthält das zweite Feld, Ewenn ein Zeichen maskiert werden muss, und das dritte Feld zeigt die maskierte Darstellung des Zeichens.

Warum ,?

Sie könnten einige Zeichen sehen, die nicht immer maskiert werden müssen, wie ,, }und {.

Also nicht immer aber irgendwann :

echo test 1, 2, 3 and 4,5.
test 1, 2, 3 and 4,5.

oder

echo test { 1, 2, 3 }
test { 1, 2, 3 }

aber Sorge:

echo test{1,2,3}
test1 test2 test3

echo test\ {1,2,3}
test 1 test 2 test 3

echo test\ {\ 1,\ 2,\ 3\ }
test  1 test  2 test  3

echo test\ {\ 1\,\ 2,\ 3\ }
test  1, 2 test  3 
F. Hauri
quelle
Dies hat das Problem, dass beim Aufrufen von pritnf über bash / sh die Zeichenfolge zuerst für bash / sh
maskiert werden
1
@ThorSummoner, nicht wenn Sie die Zeichenfolge als wörtliches Argument aus einer anderen Sprache an die Shell übergeben (wo Sie vermutlich bereits zitieren können). In Python: erhalten subprocess.Popen(['bash', '-c', 'printf "%q\0" "$@"', '_', arbitrary_string], stdin=subprocess.PIPE, stdout=subprocess.PIPE).communicate()Sie eine ordnungsgemäß in Shell zitierte Version von arbitrary_string.
Charles Duffy
1
Zu Ihrer Information, Bash's %qwar lange Zeit kaputt - Wenn mein Verstand mir gute Dienste leistet, wurde 2013 ein Fehler behoben (der aber möglicherweise immer noch kaputt sein wird), nachdem er ~ 10 Jahre lang kaputt war. Verlassen Sie sich also nicht darauf.
Jo So
@CharlesDuffy Sobald Sie sich in Python Land befinden, erledigt shlex.quote()(> = 3.3, pipes.quote()- undokumentiert - für ältere Versionen) natürlich auch die Aufgabe und erstellt eine besser lesbare Version (Hinzufügen von Anführungszeichen und Escapezeichen, falls erforderlich) der meisten Zeichenfolgen. ohne die Notwendigkeit, eine Shell zu spawnen.
Thomas Perl
1
Vielen Dank, dass Sie spezielle Hinweise zu hinzufügen ,. Ich war überrascht zu erfahren, dass eingebauter Bash printf -- %q ','gibt \,, aber /usr/bin/printf -- %q ','gibt ,(nicht entführt). Das Gleiche gilt für andere Zeichen: {, |, }, ~.
Kevinarpe
34

Um jemand anderem vor RTFM zu bewahren ... in Bash :

Zeichen in doppelten Anführungszeichen bewahrt den wörtlichen Wert aller Zeichen innerhalb der Anführungszeichen, mit Ausnahme von umschließenden $, `, \, und, wenn die Geschichte Erweiterung aktiviert ist, !.

... also, wenn Sie diesen entkommen (und natürlich dem Zitat selbst), sind Sie wahrscheinlich in Ordnung.

Wenn Sie einen konservativeren Ansatz wählen, bei dem Sie Zweifel haben, sollten Sie es vermeiden, stattdessen Zeichen mit besonderer Bedeutung zu erhalten, indem Sie nicht den Bezeichnern (dh ASCII-Buchstaben, Zahlen oder '_') entkommen. Es ist sehr unwahrscheinlich, dass diese jemals (dh in einer seltsamen POSIX-ähnlichen Shell) eine besondere Bedeutung haben und daher entkommen müssen.

Matthew
quelle
1
Hier ist das oben zitierte Handbuch: gnu.org/software/bash/manual/html_node/Double-Quotes.html
code_monk
Dies ist eine kurze, süße und meist korrekte Antwort (+1 dafür), aber vielleicht ist es sogar besser, einfache Anführungszeichen zu verwenden - siehe meine längere Antwort.
Jo So
26

Mit dieser print '%q' Technik können wir eine Schleife ausführen, um herauszufinden, welche Zeichen etwas Besonderes sind:

#!/bin/bash
special=$'`!@#$%^&*()-_+={}|[]\\;\':",.<>?/ '
for ((i=0; i < ${#special}; i++)); do
    char="${special:i:1}"
    printf -v q_char '%q' "$char"
    if [[ "$char" != "$q_char" ]]; then
        printf 'Yes - character %s needs to be escaped\n' "$char"
    else
        printf 'No - character %s does not need to be escaped\n' "$char"
    fi
done | sort

Es gibt diese Ausgabe:

No, character % does not need to be escaped
No, character + does not need to be escaped
No, character - does not need to be escaped
No, character . does not need to be escaped
No, character / does not need to be escaped
No, character : does not need to be escaped
No, character = does not need to be escaped
No, character @ does not need to be escaped
No, character _ does not need to be escaped
Yes, character   needs to be escaped
Yes, character ! needs to be escaped
Yes, character " needs to be escaped
Yes, character # needs to be escaped
Yes, character $ needs to be escaped
Yes, character & needs to be escaped
Yes, character ' needs to be escaped
Yes, character ( needs to be escaped
Yes, character ) needs to be escaped
Yes, character * needs to be escaped
Yes, character , needs to be escaped
Yes, character ; needs to be escaped
Yes, character < needs to be escaped
Yes, character > needs to be escaped
Yes, character ? needs to be escaped
Yes, character [ needs to be escaped
Yes, character \ needs to be escaped
Yes, character ] needs to be escaped
Yes, character ^ needs to be escaped
Yes, character ` needs to be escaped
Yes, character { needs to be escaped
Yes, character | needs to be escaped
Yes, character } needs to be escaped

Einige der Ergebnisse ,sehen ein wenig verdächtig aus. Es wäre interessant, @ CharlesDuffys Beiträge dazu zu erhalten.

Codeforester
quelle
2
Sie können die Antwort lesen, ,um im letzten Absatz meiner Antwort
F. Hauri
2
Denken Sie daran, dass Sie %qnicht wissen, wo in der Shell Sie das Zeichen verwenden möchten, sodass alle Zeichen ausgeblendet werden, die in einem möglichen Shell-Kontext eine besondere Bedeutung haben können. ,selbst hat keine besondere Bedeutung für sie Shell, aber wie @ F.Hauri in seiner Antwort hervorgehoben hat, hat es eine besondere Bedeutung innerhalb der Klammererweiterung{...} : gnu.org/savannah-checkouts/gnu/bash/manual/… Das ist wie! Dies erfordert auch nur eine Erweiterung in bestimmten Situationen, nicht im Allgemeinen: Funktioniert echo Hello World!einwandfrei, schlägt echo test!testjedoch fehl.
Mecki
18

Zeichen, die maskiert werden müssen, unterscheiden sich in der Bourne- oder POSIX-Shell von Bash. Im Allgemeinen ist (sehr) Bash eine Obermenge dieser Muscheln, daher sollte alles, in dem Sie entkommen shell, in Bash entkommen.

Eine nette allgemeine Regel wäre "wenn Sie Zweifel haben, entkommen Sie ihr". Aber einigen Charakteren zu entkommen, gibt ihnen eine besondere Bedeutung, wie z \n. Diese sind auf den man bashSeiten unter Quotingund aufgeführt echo.

Ansonsten entkomme jedem Zeichen, das nicht alphanumerisch ist, es ist sicherer. Ich kenne keine einzige endgültige Liste.

Die Manpages listen sie alle irgendwo auf, aber nicht an einem Ort. Lerne die Sprache, das ist der Weg, um sicher zu sein.

Eine, die mich erwischt hat, ist !. Dies ist ein Sonderzeichen (Geschichtserweiterung) in Bash (und csh), jedoch nicht in Korn Shell. Auch echo "Hello world!"gibt Probleme. Wenn Sie wie gewohnt einfache Anführungszeichen verwenden, wird die spezielle Bedeutung entfernt.

cdarke
quelle
1
Ich mag besonders den Ratschlag "Eine schöne allgemeine Regel wäre", wenn Sie Zweifel haben, entkommen Sie ihm " . Habe immer noch Zweifel, ob das Überprüfen mit sedgut genug ist, um zu sehen, ob es entkommen muss. Danke für deine Antwort!
Fedorqui 'SO hör auf zu schaden'
2
@fedorqui: Eine Überprüfung mit sedist nicht erforderlich, Sie können mit fast allem überprüfen. sedist nicht das Problem, bashist. In einfachen Anführungszeichen gibt es keine Sonderzeichen (außer einfache Anführungszeichen). Sie können dort nicht einmal Zeichen maskieren. Ein sedBefehl sollte normalerweise in einfachen Anführungszeichen stehen, da RE-Metazeichen zu viele Überlappungen mit Shell-Metazeichen aufweisen, um sicher zu sein. Die Ausnahme ist das Einbetten von Shell-Variablen, was sorgfältig durchgeführt werden muss.
Cdarke
5
Überprüfen Sie mit echo. Wenn Sie herausholen, was Sie eingegeben haben, muss es nicht entkommen. :)
Mark Reed
6

Ich nehme an, dass Sie über Bash-Strings sprechen. Es gibt verschiedene Arten von Zeichenfolgen, für deren Escape unterschiedliche Anforderungen gelten. z.B. Zeichenfolgen in einfachen Anführungszeichen unterscheiden sich von Zeichenfolgen in doppelten Anführungszeichen.

Die beste Referenz ist der Abschnitt " Zitate " des Bash-Handbuchs.

Es erklärt, welche Charaktere entkommen müssen. Beachten Sie, dass einige Zeichen möglicherweise maskiert werden müssen, je nachdem, welche Optionen aktiviert sind, z. B. die Verlaufserweiterung.

Austin Phillips
quelle
3
Es bestätigt also, dass Flucht ein solcher Dschungel ohne eine einfache Lösung ist, muss jeder Fall überprüft werden. Vielen Dank!
Fedorqui 'SO hör auf zu schaden'
@fedorqui Wie bei jeder Sprache gibt es eine Reihe von Regeln, die befolgt werden müssen. Für das Entkommen von Bash-Strings ist das Regelwerk recht klein, wie im Handbuch beschrieben. Die am einfachsten zu verwendende Zeichenfolge sind einfache Anführungszeichen, da nichts maskiert werden muss. Es gibt jedoch keine Möglichkeit, ein einfaches Anführungszeichen in eine Zeichenfolge in einfachen Anführungszeichen aufzunehmen.
Austin Phillips
@ Fedorqui. Es ist kein Dschungel. Flucht ist durchaus machbar. Siehe meinen neuen Beitrag.
Jo So
@fedorqui Sie können kein einfaches Anführungszeichen in einer Zeichenfolge in einfachen Anführungszeichen verwenden, aber Sie können es mit etwas "maskieren" wie: 'text' "'"' more text '
CR.
4

Ich habe festgestellt, dass Bash bei Verwendung der automatischen Vervollständigung automatisch einigen Zeichen entgeht.

Wenn Sie beispielsweise ein Verzeichnis mit dem Namen haben dir:A, wird bash automatisch in vervollständigtdir\:A

Auf diese Weise habe ich einige Experimente mit Zeichen der ASCII-Tabelle durchgeführt und die folgenden Listen abgeleitet:

Zeichen, die beim automatischen Vervollständigen ausgeblendet werden : (einschließlich Leerzeichen)

 !"$&'()*,:;<=>?@[\]^`{|}

Charaktere, die schlagen, entkommen nicht :

#%+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz~

(Ich habe ausgeschlossen /, da es nicht in Verzeichnisnamen verwendet werden kann)

Yuri
quelle
2
Wenn Sie wirklich eine umfassende Liste haben möchten, würde ich vorschlagen, zu prüfen, welche Zeichen printf %qgeändert werden und welche nicht, wenn sie als Argument übergeben werden - idealerweise durch den gesamten Zeichensatz.
Charles Duffy
Es gibt Fälle, in denen Sie selbst mit der Apostroph-Zeichenfolge möglicherweise Buchstaben und Zahlen maskieren möchten, um Sonderzeichen zu erzeugen. Zum Beispiel: tr '\ n' '\ t', das Zeilenumbruchzeichen in Tabulatorzeichen übersetzt.
Dick Guertin