Какой самый безопасный и простой способ сделать так, чтобы пользовательский пароль на bash стал частью программы?


12

Я ищу (1) самый безопасный и (2) самый простой способ, чтобы пользователь вводил пароль в командной строке bash и чтобы этот пароль стал частью стандартного ввода программы.

Вот как должен выглядеть 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 для принудительного ввода с терминала.
Джим Хоугленд,

1
да, у нас все еще есть проблема с двойной кавычкой и обратной косой чертой и, возможно, парой других символов. Нам нужно их кодировать, прежде чем помещать в поле двойных кавычек в BLOB-объекте JSON. Не уверен, что curl ожидает UTF-8.
Джим Хоугленд,

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, хотя вы потеряете возможность многократно использовать сохраненный пароль.
Джим Хоугленд

2

Вот решение, которое у меня есть (оно было проверено):

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

Объяснение:

  1. read -sчитает строку ввода (пароль), не отображая строку на экране. Он хранится в переменной оболочки PASSWORD.
  2. echo -n $PASSWORDставит пароль на стандартный вывод без перевода строки. (echo - встроенная команда оболочки, поэтому новый процесс не создается, поэтому (AFAIK) пароль в качестве аргумента для echo не будет показан на ps.)
  3. Perl вызывается и читает пароль из стандартного ввода в $_
  4. Perl вводит пароль в $_полный текст и выводит его на стандартный вывод

Если это происходит в 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"
Джефф Шаллер

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.