В чому проблема
По-перше, як і для багатьох утиліт, у вас виникне проблема з іменами файлів, починаючи з -
. Перебуваючи в:
sh -c 'inline sh script here' other args
Інші аргументи передаються до inline sh script
; з perl
еквівалентом,
perl -e 'inline perl script here' other args
Інші аргументи перевіряються на наявність більш варіантів PERL перший, не інлайн сценарію. Так, наприклад, якщо -eBEGIN{do something evil}
в поточному каталозі є файл, який називається ,
perl -ne 'inline perl script here;' *
(з чи без -n
) зробить щось зле.
Як і для інших утиліт, вирішення цього завдання - використання маркера кінця параметрів ( --
):
perl -ne 'inline perl script here;' -- *
Але навіть тоді це все ще небезпечно, і це стосується <>
оператора, який використовує -n
/ -p
.
Проблема пояснюється в perldoc perlop
документації.
Цей спеціальний оператор використовується для зчитування одного рядка (один запис; записи за замовчуванням є рядками) вводу, де цей вхід надходить з кожного переданого аргументу по черзі @ARGV
.
В:
perl -pe '' a b
-p
передбачає while (<>)
цикл навколо коду (тут порожній).
<>
спочатку відкриється a
, прочитає записи по одному рядку, поки файл не вичерпається, а потім відкриється b
...
Проблема полягає в тому, що для відкриття файлу він використовує першу, небезпечну форму open
:
open ARGV, "the file as provided"
З цією формою, якщо аргумент є
"> afile"
, відкривається afile
в режимі письма,
"cmd|"
, він працює cmd
і читає його вихід.
"|cmd"
, у вас відкритий потік для запису на вхід cmd
.
Так, наприклад:
perl -pe '' 'uname|'
Не виводить вміст файла, що називається uname|
(ідеально правильне ім'я файлу btw), але вихід uname
команди.
Якщо ви працюєте:
perl -ne 'something' -- *
І хтось створив файл з назвою rm -rf "$HOME"|
(знову-таки цілком правильне ім'я файлу) у поточному каталозі (наприклад, через те, що цей каталог колись могли записувати інші, або ви витягнули хитромудрий архів, або ви виконали якусь команду dodgy, або була використана інша вразливість у якомусь іншому програмному забезпеченні), то ви великі проблеми. Сфери, де важливо знати про цю проблему, - це інструменти, які автоматично обробляють файли в публічних областях /tmp
(наприклад, інструменти, які можуть бути викликані такими інструментами).
Файли називаються > foo
, foo|
, |foo
є проблемою. Але в меншій мірі < foo
і foo
з провідними або кінцевими символами інтервалів ASCII (включаючи пробіл, вкладку, новий рядок, cr ...), а це означає, що ці файли не будуть оброблені, або неправильний буде.
Також майте на увазі, що деякі символи в деяких багатобайтових наборах символів (наприклад, ǖ
у BIG5-HKSCS) закінчуються на байт 0x7c, кодування |
.
$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000 88 7c
210 |
0000002
Тож у локалі, що використовують цю гарнітуру,
perl -pe '' ./nǖ
Спробував би запустити ./n\x88
команду , як perl
би НЕ намагатися інтерпретувати це ім'я файлу локалі користувача!
Як виправити / обійти
AFAIK, ви нічого не можете зробити, щоб змінити таку небезпечну поведінку за замовчуванням perl
раз і назавжди для всієї системи.
По-перше, проблема виникає лише з символами на початку та в кінці імені файлу. Отже, хоча perl -ne '' *
або perl -ne '' *.txt
є проблемою,
perl -ne 'some code' ./*.txt
не тому , що всі аргументи тепер починаються з ./
і закінчуються .txt
(так не -
, <
, >
, |
, космос ...). В цілому, це хороша ідея , щоб Приставка кульки з ./
. Це також дозволяє уникнути проблем з файлами, що викликаються -
або починаються з -
багатьох інших утиліт (і ось, це означає, що вам більше не потрібен --
маркер кінця параметрів ( ).
Використання -T
для включення taint
режиму допомагає певною мірою. Вона скасує команду, якщо зустрінеться такий шкідливий файл (лише для випадків >
і |
випадків, а не <
пробілів).
Це корисно при інтерактивному використанні таких команд, оскільки це попереджає про те, що відбувається щось хитре. Це може виявитися небажаним при автоматичній обробці, оскільки це означає, що хтось може зробити цю обробку невдалою, просто створивши файл.
Якщо ви хочете обробити кожен файл, незалежно від їх імені, ви можете використовувати в ARGV::readonly
perl
модуль на CPAN ( до жаль , як правило , не встановлюється за умовчанням). Це дуже короткий модуль, який робить:
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
};
};
В основному, це sanitizes @ARGV, перетворюючи, " foo|"
наприклад, на "< ./ foo|\0"
.
Ви можете зробити те ж саме у BEGIN
заяві у вашій perl -n/-p
команді:
perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
Тут ми спростимо його на основі припущення, ./
яке використовується.
Побічним ефектом цього (та ARGV::readonly
), хоча, є те, що $ARGV
в your code here
показах, що слідує символ NUL.
Оновити 2015-06-03
perl
v5.21.5 і вище є новий <<>>
оператор, який поводиться так, <>
за винятком того, що він не буде виконувати цю спеціальну обробку. Аргументи вважатимуться лише іменами файлів. Тож із цими версіями тепер можна писати:
perl -e 'while(<<>>){ ...;}' -- *
(не забудьте --
або не використовуйте ./*
), не боячись перезаписати файли або виконати несподівані команди.
-n
/ -p
все ж використовуйте небезпечну <>
форму. І будьте обережні посилання, як і раніше, дотримуйтесь, так що це не означає, що це безпечно використовувати у ненадійних каталогах.