Wie kann ich angesichts des folgenden Skripts sicherstellen, dass das Argument nur einen gültigen Dateinamen enthält /home/charlesingalls/
und keinen Pfad ( ../home/carolineingalls/
) oder Platzhalter usw.?
Ich möchte nur, dass das Skript eine einzelne Datei aus dem angegebenen fest codierten Verzeichnis löschen kann. Dieses Skript wird als privilegierter Benutzer ausgeführt.
#!/bin/bash
rm -f /home/charlesingalls/"$1"
/
. Platzhalter werden nicht in Anführungszeichen interpretiert.-r
mitrm
.rm -r
dient zum rekursiven Löschen eines Verzeichnisses und aller darunter liegenden Dateien und Verzeichnisse. Dies ist nur beim Löschen von Verzeichnissen hilfreich. Im Allgemeinen nicht Frachtkult. Kopieren Sie also nicht nur Dinge, die nützlich erscheinen, in Ihre Befehlszeile oder Ihr Skript, ohne zu verstehen, was sie tun oder wie sie funktionieren. Die Flugzeuggötter, die die magische Fracht bringen, können wütend werden und alle Ihre Dateien löschen.Antworten:
Wenn Sie nur eine Datei in
/home/charlesingalls
(und keine Datei in einem Unterverzeichnis) löschen möchten, ist dies ganz einfach: Überprüfen Sie einfach, ob das Argument kein enthält/
.Dies läuft
rm
auch , wenn das Argument.
oder..
oder leer, aber in diesem Fallrm
wird harmlos ausfallen , ein Verzeichnis zu löschen.Platzhalter sind hier nicht relevant, da keine Platzhaltererweiterung durchgeführt wird.
Dies ist auch bei Vorhandensein symbolischer Links sicher: Wenn es sich bei der Datei um einen symbolischen Link handelt, wird der Symlink (der sich in befindet
/home/charlesingalls
) entfernt und das Ziel dieses Links wird nicht beeinflusst.Beachten Sie, dass dies voraussetzt, dass
/home/charlesingalls
dies nicht verschoben oder geändert werden kann. Dies sollte in Ordnung sein, wenn das Verzeichnis im Skript fest codiert ist. Wenn es jedoch aus Variablen ermittelt wird, ist die Bestimmung zum Zeitpunkt derrm
Ausführung des Befehls möglicherweise nicht mehr gültig .Basierend auf den zusätzlichen Informationen, dass das Argument ein virtueller Hostname ist, sollten Sie eine Whitelist anstelle einer Blacklist durchführen: Überprüfen Sie, ob der Name ein sinnvoller virtueller Hostname ist, anstatt nur Schrägstriche zu verbieten. Ich überprüfe, ob der Name mit einem Kleinbuchstaben oder einer Ziffer beginnt und keine anderen Zeichen als Kleinbuchstaben, Ziffern, Punkte und Bindestriche enthält.
quelle
.
Diese Antwort setzt voraus, dass
$1
Unterverzeichnisse enthalten sein dürfen. Wenn Sie an dem einfacheren Fall interessiert sind, bei dem es$1
sich um einen einfachen Verzeichnisnamen handeln sollte, lesen Sie eine der anderen Antworten.Platzhalter werden in doppelten Anführungszeichen nicht erweitert. Da
$1
es sich um doppelte Anführungszeichen handelt, sind Platzhalter kein Problem.Sowohl
../
als auch Symlinks können den tatsächlichen Speicherort einer Datei verdecken . Im Folgenden werden Tests gezeigt, um festzustellen, ob sich die Datei wirklich und nicht nur scheinbar unter dem gewünschten Pfad befindet.Neuere Systeme: mit
realpath
Um herauszufinden, ob die Datei wirklich unter ist
/home/charlesingalls/
oder nicht, können Sie Folgendes verwendenrealpath
:Das Obige wird ausgeführt,
exit 1
wenn sich die von angegebene Datei an einer$1
anderen Stelle als im Verzeichnis befindet/home/charlesingalls/
.realpath
kanonisiert den gesamten Pfad und eliminiert sowohl Symlinks als auch../
.realpath
ist Teil von GNU coreutils und sollte auf jedem Linux-System verfügbar sein.realpath
erfordert GNU Coreutils 8.15 (Jan 2012) oder besser .Beispiele
Um zu demonstrieren, wie realpath folgt
../
, um den tatsächlichen Speicherort einer Datei zu bestimmen (z. B. wird die-q
Option grep weggelassen, damit die tatsächliche Ausgabe von grep sichtbar ist):Um zu demonstrieren, wie es Symlinks folgt:
Ältere Systeme: mit
readlink -e
readlink
ist auch in der Lage, einen Pfad zu kononisieren, indem sowohl Symlinks als auch../
:Verwenden der gleichen Beispieldateien:
Versionen von
readlink
sind nicht nur auf älteren GNU-Systemen verfügbar, sondern auch auf BSD.quelle
coreutils
hat nichtrealpath
-f
("alle bis auf die letzte Komponente müssen vorhanden sein") und die verwendeten Beispiele-e
("alle Komponenten müssen vorhanden sein"), was etwas verwirrend ist.rm
auf das Argument selbst und nicht auf dessen Ziel einwirken.grep -q
diesegrep
Option , um die Ausgabe übereinstimmender Zeilen zu verhindern. Sie erhalten immer noch den Exit-Status&&
und arbeiten||
immer noch genau so, wie Sie es gewohnt sind.Wenn Sie Pfade vollständig verbieten möchten, können Sie am einfachsten testen, ob die Variable einen Schrägstrich (
/
) enthält. In Bash:Dies blockiert jedoch alle Pfade, einschließlich
foo/bar
. Sie könnten..
stattdessen testen , aber das würde die Möglichkeit lassen, dass Symlinks auf Verzeichnisse außerhalb des Zielpfads verweisen.Wenn Sie nur das Löschen einer einzelnen Datei zulassen möchten, sollten Sie diese nicht verwenden
rm -r
.Abhängig davon, was Sie tun, können Sie auch die Dateiberechtigungen des Systems verwenden, um nur das Löschen von Dateien zuzulassen, die der Benutzer selbst löschen kann. Etwas wie das:
Obwohl @Gilles dies kommentierte, gibt es ein Problem mit Anführungszeichen: Es schlägt fehl, wenn es
$1
ein einfaches Anführungszeichen enthält. Daher sollte die Variable zuerst darauf getestet werden (z. B.if [[ "$1" = *\'* ]] ; then fail...
durch Whitelist eines sinnvollen Zeichensatzes) oder der übergebene Dateiname eine Umgebungsvariable mit zquelle
su
Befehl fehlerhaft ist, weil das Zitat falsch ist. Sie führen einen Befehl aus, dessen Argument als Shell-Snippet interpoliert ist. Wenn das Argument beispielsweise lautet,$(touch foo)
wird Ihr Code ausgeführttouch foo
.'
durch ersetzen'\''
) oder sie durch einen anderen Kanal wie eine Umgebungsvariable leiten (was ich hier tun würde :)file_to_remove="$1" su -c 'rm "/home/charlesingalls/$file_to_remove"'
. Es stellt sich heraus, dass der Dateiname ein virtueller Hostname sein soll, daher wäre es auch hier in Ordnung, alle Sonderzeichen abzulehnen.