Що є найбільш безпечним і найпростішим способом, щоб введений користувачем пароль на bash став частиною stdin до програми?


12

Я шукаю (1) найбезпечніший і (2) найпростіший спосіб надати користувачеві пароль підказки на bash shell і щоб цей пароль став частиною stdin програми.

Ось як повинен виглядати stdin: {"username":"myname","password":"<my-password>"}де <my-password>саме те, що було введено в підказку оболонки. Якби я мав контроль над програмою stdin, то я міг би змінити її, щоб надійно запросити пароль і поставити його на місце, але низхідна частина - це стандартна команда загального призначення.

Я розглянув та відхилив підходи, які використовують наступне:

  • користувач вводить пароль у командному рядку: пароль буде показаний на екрані і також буде видимий для всіх користувачів через "ps"
  • інтерполяція змінної оболонки в аргумент зовнішньої програми (наприклад, ...$PASSWORD...): пароль все одно буде видно всім користувачам через "ps"
  • змінні середовища (якщо вони залишаються в оточенні): пароль буде видимим для всіх дочірніх процесів; навіть надійні процеси можуть викрити пароль, якщо вони скидають змінні ядра чи дамп-середовища як частину діагностики
  • пароль, який тривалий час сидить у файлі, навіть файл із чіткими дозволами: користувач може випадково викрити пароль, а користувач root може випадково побачити пароль

Я поставлю своє поточне рішення як відповідь нижче, але з радістю виберу кращу відповідь, якщо хтось придумає його. Я думаю, що має бути щось простіше, або, можливо, хтось бачить стурбованість безпекою, яку я пропустив.


(Повний випадок використання полягає в тому, що пароль потрібен частині тіла POST на сервері HTTPS, але я не хочу робити це питання надто конкретним. Stdin повинен стати тілом POST через curl "-d @ - ".)
Джим Хогланд

Відповіді:


14

З bashабо zsh:

unset -v password # make sure it's not exported
set +o allexport  # make sure variables are not automatically exported
IFS= read -rs password < /dev/tty &&
  printf '{"username":"myname","password":"%s"}\n' "$password" | cmd

Без IFS=, readпозбавить початкові і кінцеві пробіли з пароля ви набираєте.

Без -rцього вона оброблятиме зворотні косої риси як символу, що цитує.

Ви хочете переконатися, що читаєте лише з терміналу.

echoне можна надійно використовувати. В bashі zsh, printfпобудований таким чином, щоб командний рядок не відображався на виході ps.

У цьому випадку bashпотрібно вказати, $passwordяк інакше до нього застосований оператор split + glob .

Це все ще неправильно, хоча вам потрібно буде кодувати цей рядок як JSON. Наприклад, проблема з подвійним котируванням та зворотною косою рисою принаймні буде проблемою. Вам, мабуть, потрібно потурбуватися про кодування цих символів. Чи очікує ваша програма рядків UTF-8? Що надсилає ваш термінал?

Щоб додати рядок підказок, натисніть zsh:

IFS= read -rs 'password?Please enter a password: '

З bash:

IFS= read -rsp 'Please enter a password: ' password

printf - ось що нам потрібно, щоб уникнути виклику perl - хороша порада. Добре, що у вас є unset passwordі set +aтам, хоча це можна пропустити, якщо ви можете зробити більше припущень щодо поточної оболонки. Хороша думка про варіант -r для читання та IFS=випередження для кращого простору із різноманітними паролями. Добре йти з / dev / tty, щоб примусити вхід з терміналу.
Jim Hoagland

1
так, у нас все ще є проблема з подвійною котировкою та зворотною косою рисою, можливо, ще з кількома символами. Нам потрібно зашифрувати їх, перш ніж помістити всередину поля з подвійним цитуванням у блоці JSON. Не впевнений, чи очікує завиток UTF-8.
Jim Hoagland

1
python3 -c 'import json; print(json.dumps({"username": "myname", "password": input()}))', якщо у вас лежить Python. Для Python 2, s / input / raw_input /. Якщо ви передасте пароль у цю команду, вона виплюне відповідний JSON.
Кевін

Кевін, це було б гарною пропозицією, якщо ми зможемо надійно ввести пароль у stdin і не проти закликати python. Це будує JSON на льоту. Це буде добре, якщо використовувати getpass.getpass () всередині Python, хоча ви втрачаєте можливість продовжувати використовувати збережений пароль кілька разів.
Jim Hoagland

2

Ось таке рішення, яке у мене є (воно перевірено):

$ read -s PASSWORD
<user types password>
$ echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"'

Пояснення:

  1. read -sчитає рядок stdin (пароль), не повторюючи рядок на екрані. Він зберігає перемінну оболонку PASSWORD.
  2. echo -n $PASSWORDставить пароль на stdout без будь-якого нового рядка. (echo - це вбудована команда оболонки, тому новий процес не створюється, тому (AFAIK) пароль як аргумент ехо не відображатиметься на ps.)
  3. perl викликається і читає пароль від stdin до $_
  4. perl вводить пароль у $_повний текст і роздруковує його до stdout

Якщо це буде переходити до HTTPS POST, другий рядок буде приблизно таким:

echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"' | curl -H "Content-Type: application/json" -d@- -X POST <url-to-post-to>

3
Це виглядає досить добре. Я лише зазначу, що немає жодних причин посилатися на perl; ви можете прекрасно зробити щось подібне printf '{"username":"myname","password":"%s"}' $PASSWORD, що зберігає ті самі властивості безпеки та економить зовнішній процес.
Том Хант,

2
Якщо ви хочете узагальнити рішення для readкоманд, які не підтримують прапор -s, ви можете використовувати "stty -echo; читати PASSWORD; stty echo"
Jeff Schaller

2
Труба починає два нові процеси, по одному для кожної сторони. Використовуйте тут-рядок замість: perl -e '...' <<< "$PASSWORD".
чепнер

2
@chepner, <<<в bashмагазинах вміст в тимчасовий файл , який гірше.
Стефан Шазелас

1
Згідно з TLDP, він створює файл, але його не читають жодні інші процеси. "Тут документи створюють тимчасові файли, але ці файли видаляються після відкриття та не доступні жодному іншому процесу." tldp.org/LDP/abs/html/here-docs.html відразу після прикладу 19-12.
dragon788

0

Якщо ваша версія readне підтримує, -sви спробуйте сумісний з POSIX спосіб, який бачите у цій відповіді .

echo -n "USERNAME: "; read uname
echo -n "PASSWORD: "; set +a; stty -echo; read passwd; command <<<"$passwd"; set -a; stty echo; echo
passwd= # get rid of passwd possibly only necessary if running outside of a script

set +aПовинен запобігти автоматичний «експорт» змінної в навколишнє середовище. Слід ознайомитись із довідковими сторінками. sttyЄ багато варіантів. <<<"$passwd"Котирується , тому що хороші паролі можуть мати прогалини. Останнє echoпісля ввімкнення параметра stty echo- це запуск наступної команди / виводу в новому рядку.

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