Захист команди оболонки за допомогою змінної рядка


9

В рамках мови програмування я виконую просту команду оболонки

cd var; echo > create_a_file_here

при цьому Var є змінною, яка містить рядок (сподіваюсь) каталогу до місця, де я хочу створити файл "create_a_file_here". Тепер, якщо хтось бачить цей рядок коду, його можна використовувати, призначивши, наприклад:

var = "; rm -rf /"

Речі можуть стати досить потворними. Одним із способів уникнути вищезазначеного випадку може бути пошук рядка в var для деяких спеціальних символів, таких як ';' перед виконанням команди оболонки, але я сумніваюся, це охоплює всі можливі подвиги.

Хтось знає хороший спосіб переконатися, що "cd var" змінює лише каталог і більше нічого?


4
Залежно від того, як можна викликати оболонку, ви також можете передавати varяк аргумент. Наприклад виклик shз аргументами -c, 'cd "$1"; echo > create_a_file_here', 'sh', varробіт і не вимагає яких - небудь змін var. 'sh'Аргумент передається як $0.
ipsec

1
Яка мова програмування? Використовуєте POSIX shчи створюєте власну мову програмування з подібним синтаксисом, але яка розширюється, varа не вимагає від вас запису cd "$var"? Або це bashз shopt -s cdable_vars? О, я думаю, ви маєте на увазі, що якась інша програма висуває оболонку для виконання цих команд. Тож просто цитуйте var, але переконайтеся, що він не містить самого цитата…
Пітер Кордес

@PeterCordes Якщо ви говорите про Bash, добре змінюється подвійна змінна зміна, яка містить подвійну цитату. наприклад, s='"'; echo "$s"відбитки ".
wjandrea

@WJAndrea: так, але немає жодної цитати "козирної картки", яка не може бути переможена при побудові змінної задачі з ненадійного введення. О, рішення: виконайте var=untrusted stringв батьківській програмі, так varце змінна середовище, яка вже встановлена ​​при виклику sh. Тоді вам потрібно просто цитувати це кожного разу, коли ви розширюєте це, що можна зробити надійно. Ах, я бачу, що ця ідея вже є частиною відповіді Стефана>. <
Пітер Кордес

Відповіді:


9

Якщо я правильно розумію, varце змінна у вашій мові програмування.

А мовою програмування ви просите оболонку інтерпретувати рядок, що є конкатенацією "cd ", вмістом цієї змінної та "; echo > create_a_file_here".

Якщо тоді, так, якщо вміст varне контролюється жорстко, це вразливість введення команд.

Ви можете спробувати і належним чином процитувати вміст змінної¹ у синтаксисі оболонки, щоб гарантовано передати її як єдиний аргумент cdвбудованому.

Іншим підходом було б передати зміст цієї змінної іншим способом. Очевидним способом було б передати це в змінну середовища. Наприклад, в C:

char *var =  "; rm -rf /";
setenv("DIR", var, 1);
system("CDPATH= cd -P -- \"$DIR\" && echo something > create_a_file_here");

Цього разу код, який ви просите інтерпретувати оболонку, виправлений, нам все одно потрібно правильно записати його в синтаксис оболонки (тут передбачається, що оболонка, сумісна з POSIX):

  • Розширення змінної оболонки повинно бути вказано для запобігання спліт + glob
  • вам потрібно -Pдля cdзробити простийchdir()
  • вам потрібно --позначити кінець варіантів, щоб уникнути проблем із varпочатком -(або +в деяких оболонках)
  • Ми встановлюємо CDPATHпорожній рядок на випадок, якщо він знаходився в оточенні
  • Ми виконуємо echoкоманду лише в тому випадку, коли вона cdбула успішною.

Існує (принаймні) одна проблема: якщо вона varє -, вона не chdir в каталог, який називається, -а в попередній каталог (як збережено в $OLDPWD), і OLDPWD=- CDPATH= cd -P -- "$DIR"не гарантується, що він обійдеться. Тож вам знадобиться щось на кшталт:

system(
  "case $DIR in\n"
  " (-) CDPATH= cd -P ./-;;\n"
  " (*) CDPATH= cd -P -- \"$DIR\";;\n"
  "esac && ....");

Зауважте, що просто робити system(concat("cd \"", var, "\"; echo..."));це не спосіб, ви просто пересунете проблему.

Наприклад, var = "$(rm -rf /)"проблема все ще буде проблемою.

Тільки надійний спосіб процитувати текст для Bourne-подібних оболонок полягає в одинарні лапки , а також дбати про одинарні лапки , які можуть статися в рядку. Наприклад, поверніть char *var = "ab'cd"на char *escaped_var = "'ab'\\''cd'". Тобто замініть все 'на '\''і загорніть всю річ всередину '...'.

Це по- , як і раніше передбачає , що цей рядок в лапках не використовується в зворотних лапках, і ви по- , як і раніше потрібно --, -P, &&, CDPATH=...


12

Просте рішення: Не викликайте оболонку з вашої програми. Зовсім.

Ваш приклад тут тривіальний, змінювати каталог та створювати файли слід просто на будь-якій мові програмування. Але навіть якщо вам потрібно запустити зовнішню команду, зазвичай це не потрібно робити через оболонку.

Так, наприклад, в Python, замість запуску os.system("somecmd " + somearg), використовуйте subprocess.run(["somecmd", somearg]). В C замість цього system()використовуйте fork()та exec()(або знайдіть бібліотеку, яка це робить).

Якщо вам потрібно скористатися оболонкою, цитуйте аргументи командного рядка або передавайте їх через оточення, як у відповіді Stéphane . Крім того, якщо ви переживаєте за особливих символів, правильне рішення - не намагатися відфільтрувати (чорний список) потенційно небезпечних символів, а лише зберегти символи, які, як відомо, є безпечними (білий список).

Дозвольте лише тим персонажам, функції яких ви знаєте, таким чином менше ризику чогось пропустити. Кінцевим результатом може бути те, що ви вирішите лише дозволити [a-zA-Z0-9_], але цього може бути достатньо, щоб виконати роботу. Ви також можете перевірити, чи ваш локальний набір та набір інструментів не містять наголошених букв, таких як äі öв цьому. Вони, мабуть, не вважаються спеціальними жодною оболонкою, але знову ж таки, краще бути впевненим, проходять вони чи ні.


1
І переконайтеся, що все, що ви використовуєте для порівняння [a-zA-Z0-9], не включає речі, такі à, кодування яких також може бути неправильно інтерпретовано деякими оболонками (наприклад bash) у деяких регіонах.
Стефан Шазелас

@ StéphaneChazelas (не цікаво :) чи вони (і саме під тими, де вони постраждали?)
Вільф

10

У мові програмування повинні бути кращі способи робити речі, ніж виконання команд оболонки. Наприклад, заміна cd varна еквівалент вашої мови програмування chdir (var);повинна гарантувати, що будь-яка хитрість із значенням varлише призводить до помилки "Каталог не знайдена", а не випадкових та, можливо, шкідливих дій.

Також ви можете використовувати абсолютні шляхи, а не змінювати каталоги. Просто з'єднайте ім'я каталогу, косу рису та ім'я файлу, яке ви хочете використовувати.

У C я можу зробити щось на кшталт:

char filepath[PATH_MAX];  /* alternative constant: MAXPATHLEN */

/* Join directory name in var and the filename, guarding against exceeding PATH_MAX */
snprintf (filepath, PATH_MAX, "%s/%s", var, "create_a_file_here");

/* create an empty file/truncate an existing one */
fclose (fopen (filepath, "w") );

Невже ваша мова програмування може зробити щось подібне?


Дякую за вашу відповідь, але, на жаль, мені доведеться використовувати вирішення команд bash. Я тестував цитування змінної - як запропонував Муру - і, здається, працює!
ES___
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.