Jaki jest problem
Po pierwsze, podobnie jak w przypadku wielu narzędzi, będziesz mieć problem z nazwami plików zaczynającymi się od -
. Podczas gdy w:
sh -c 'inline sh script here' other args
Pozostałe argumenty są przekazywane do inline sh script
; z perl
odpowiednikiem
perl -e 'inline perl script here' other args
Pozostałe argumenty są skanowane w poszukiwaniu najpierw większej liczby opcji perla , a nie wbudowanego skryptu. Na przykład jeśli -eBEGIN{do something evil}
w bieżącym katalogu znajduje się plik o nazwie
perl -ne 'inline perl script here;' *
(z lub bez -n
) zrobi coś złego.
Podobnie jak w przypadku innych narzędzi, obejście tego polega na użyciu znacznika końca opcji ( --
):
perl -ne 'inline perl script here;' -- *
Ale nawet wtedy jest to nadal niebezpieczne i zależy to od <>
operatora używanego przez -n
/ -p
.
Problem wyjaśniono w perldoc perlop
dokumentacji.
Ten specjalny operator służy do odczytu jednego wiersza (jeden rekord, domyślnie zapisanych wierszy) danych wejściowych, gdzie dane wejściowe pochodzą z każdego przekazywanego argumentu @ARGV
.
W:
perl -pe '' a b
-p
zakłada while (<>)
pętlę wokół kodu (tutaj pusta).
<>
najpierw otworzy się a
, odczyta rekordy pojedynczo, aż do wyczerpania pliku, a następnie otworzy b
...
Problem polega na tym, że do otwarcia pliku używa pierwszej, niebezpiecznej formy open
:
open ARGV, "the file as provided"
W tej formie, jeśli argument jest
"> afile"
, otwiera się afile
w trybie pisania,
"cmd|"
, działa cmd
i odczytuje dane wyjściowe.
"|cmd"
, masz strumień otwarty do zapisu na wejściu cmd
.
Na przykład:
perl -pe '' 'uname|'
Nie wyświetla treści pliku o nazwie uname|
(doskonale poprawna nazwa pliku btw), ale dane wyjściowe uname
polecenia.
Jeśli biegasz:
perl -ne 'something' -- *
I ktoś utworzył plik o nazwie rm -rf "$HOME"|
(ponownie doskonale poprawna nazwa pliku) w bieżącym katalogu (na przykład dlatego, że ten katalog był kiedyś zapisywalny przez innych, lub wyodrębniłeś podejrzane archiwum, lub uruchomiłeś jakieś podejrzane polecenie lub została wykorzystana inna luka w zabezpieczeniach innego oprogramowania), wtedy masz duże problemy. Obszary, w których należy zdawać sobie sprawę z tego problemu, to narzędzia przetwarzające pliki automatycznie w miejscach publicznych , takie jak /tmp
(lub narzędzia, które mogą być wywoływane przez takie narzędzia).
Pliki zwane > foo
, foo|
, |foo
są problemem. Ale w mniejszym stopniu < foo
i foo
z czołowymi lub końcowe znaki odstępów ASCII (w tym miejsca, tabulacjami, Cr ...), a także, że środki te pliki nie będą przetwarzane lub niewłaściwy z nich będzie.
ǖ
Pamiętaj też, że niektóre znaki w niektórych zestawach znaków wielobajtowych (jak w BIG5-HKSCS) kończą się bajtem 0x7c, kodowaniem |
.
$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000 88 7c
210 |
0000002
Więc w lokalizacjach używających tego zestawu znaków,
perl -pe '' ./nǖ
Spróbuje uruchomić ./n\x88
komendę jak perl
by nie spróbować zinterpretować tę nazwę pliku w lokalizacji użytkownika!
Jak to naprawić / obejść
AFAIK, nic nie możesz zrobić, aby zmienić to niebezpieczne zachowanie domyślne perl
raz na zawsze w całym systemie.
Po pierwsze, problem występuje tylko w przypadku znaków na początku i na końcu nazwy pliku. Tak więc, podczas gdy perl -ne '' *
lub perl -ne '' *.txt
stanowią problem,
perl -ne 'some code' ./*.txt
nie dlatego, że wszystkie argumenty, teraz zaczynają się ./
i kończą w .txt
(więc nie -
, <
, >
, |
, przestrzeń ...). Mówiąc bardziej ogólnie, że to dobry pomysł, aby prefiks globs z ./
. Pozwala to również uniknąć problemów z plikami wywoływanymi -
lub rozpoczynającymi się -
od wielu innych narzędzi (a tutaj oznacza to, że nie potrzebujesz już --
znacznika końca opcji ( )).
Używanie trybu -T
włączania taint
pomaga w pewnym stopniu. Przerywa polecenie, jeśli napotka taki złośliwy plik (tylko dla przypadków >
i |
, ale nie <
lub białych znaków).
Jest to przydatne, gdy używasz takich poleceń interaktywnie, ponieważ ostrzega Cię, że dzieje się coś podejrzanego. Może to nie być pożądane podczas automatycznego przetwarzania, ponieważ oznacza to, że ktoś może spowodować, że przetwarzanie nie powiedzie się, po prostu przez utworzenie pliku.
Jeżeli chcesz, aby przetworzyć każdy plik, niezależnie od ich nazwy, można korzystać z ARGV::readonly
perl
modułu na CPAN (niestety zwykle nie zainstalowanego domyślnie). To bardzo krótki moduł, który:
sub import{
# Tom Christiansen in Message-ID: <24692.1217339882@chthon>
# reccomends essentially the following:
for (@ARGV){
s/^(\s+)/.\/$1/; # leading whitespace preserved
s/^/< /; # force open for input
$_.=qq/\0/; # trailing whitespace preserved & pipes forbidden
};
};
Zasadniczo dezynfekuje @ARGV, zmieniając " foo|"
na przykład w "< ./ foo|\0"
.
Możesz zrobić to samo w BEGIN
poleceniu w swoim perl -n/-p
poleceniu:
perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
Tutaj upraszczamy to przy założeniu, że ./
jest używane.
Efektem ubocznym, że (i ARGV::readonly
) jest jednak, że $ARGV
w your code here
pokazach że końcowe znak NUL.
Aktualizacja 2015-06-03
perl
Wer. 5.21.5 i nowsze mają nowego <<>>
operatora, który zachowuje się tak, <>
z tym wyjątkiem, że nie wykona tego specjalnego przetwarzania. Argumenty będą traktowane tylko jako nazwy plików. Dzięki tym wersjom możesz teraz pisać:
perl -e 'while(<<>>){ ...;}' -- *
(nie zapomnij --
lub użyj ./*
) bez obawy, że nadpisze pliki lub uruchomi nieoczekiwane polecenia.
-n
/ -p
nadal jednak używam niebezpiecznej <>
formy. Uważaj też na dowiązania symboliczne, dlatego niekoniecznie oznacza to, że można bezpiecznie korzystać z niezaufanych katalogów.
Oprócz odpowiedzi @ Stéphane Chazelas nie musimy się martwić tym problemem, jeśli użyjemy
-i
opcji wiersza poleceń:Ponieważ przy użyciu
-i
opcjiperl
stosowany stat , aby sprawdzić stan pliku przed procesem IT:źródło
stat
czekiem a efektywnym przetwarzaniem perla, który ma miejsce zaraz po nim?stat
. Chodzi tylko o-i
to, aby edytować pliki w miejscu, więc nie ma sensu akceptować argumentów innych niż rzeczywiste ścieżki plików, więc dzięki-i
temu specjalne przetwarzanie nie jest wykonywane.