Как передать пароль дочернему процессу?

18

Передача пароля в командной строке (дочернему процессу, запущенному из моей программы), как известно, небезопасна (потому что его могут увидеть даже другие пользователи с помощью команды ps). Можно ли передавать его как переменную среды?

Что еще я могу использовать, чтобы передать его? (За исключением переменной среды) кажется, что самое простое решение - использовать канал, но это самое простое решение - нелегкое.

Я программирую на Perl.

Porton
источник
2
Почему это не так просто? Это не обязательно должен быть отдельный / именованный канал, просто обычный stdin / out подойдет ... это не должно вызывать особых проблем на любом языке. Вы можете поместить его в простой конфигурационный файл, если вы можете быть уверены, что он читается только интересующими вас процессами (что намного сложнее, чем кажется).
frostschutz
1
Если вы не вызываете exec у ребенка, у вас все еще есть копия пароля без необходимости что-либо делать.
Джеймс Янгман

Ответы:

26

Аргументы процесса видны всем пользователям, но среда видна только одному и тому же пользователю ( по крайней мере, в Linux , и я думаю о каждом современном варианте Unix). Поэтому передача пароля через переменную среды безопасна. Если кто-то может читать переменные вашей среды, он может выполнять процессы как вы, так что игра уже окончена.

Содержимое среды подвержено некоторому риску косвенной утечки, например, если вы запускаете psчто-то для исследования и случайно копируете результат, включая конфиденциальные переменные среды, в публичном месте. Другой риск заключается в том, что вы передаете переменную среды программе, которая в ней не нуждается (включая дочерние элементы процесса, которому требуется пароль), и эта программа предоставляет свои переменные среды, потому что она не ожидала, что они будут конфиденциальными. Насколько серьезны эти риски вторичной утечки, зависит от того, что делает процесс с паролем (как долго он работает? Он запускает подпроцессы?).

Проще убедиться, что пароль не будет случайно утек, передав его по каналу, который не предназначен для прослушивания, например по каналу. Это довольно легко сделать на отправляющей стороне. Например, если у вас есть пароль в переменной оболочки, вы можете просто сделать

echo "$password" | theprogram

если theprogramожидает пароль на его стандартный ввод. Обратите внимание, что это безопасно, потому что echoявляется встроенным; это не будет безопасно с внешней командой, так как аргумент будет представлен в psвыводе. Еще один способ добиться того же эффекта - вот документ:

theprogram <<EOF
$password
EOF

Некоторым программам, которым требуется пароль, может быть предложено прочитать его из определенного дескриптора файла. Вы можете использовать дескриптор файла, отличный от стандартного ввода, если вам нужен стандартный ввод для чего-то другого. Например, с gpg:

get-encrypted-data | gpg --passphrase-fd 3 --decrypt … 3<<EOP >decrypted-data
$password
EOP

Если программе нельзя сказать, чтобы она читала из файлового дескриптора, но можно было бы сказать, чтобы она читала из файла, вы можете сказать ей, чтобы она читала из файлового дескриптора, используя имя файла, например `/ dev / fd / 3.

theprogram --password-from-file=/dev/fd/3 3<<EOF
$password
EOF

В ksh, bash или zsh вы можете сделать это более кратко с помощью подстановки процессов.

theprogram --password-from-file=<(echo "$password")
Жиль "ТАК - прекрати быть злым"
источник
В Solaris 9 и более ранних /usr/ucb/psверсиях был установлен root, поэтому он мог читать и отображать переменные окружения других процессов - это было удалено в Solaris 10, поэтому приведенный выше ответ «каждый второй современный вариант Unix» относится к выпускам Solaris 2005 и более поздних версий.
Алан
@alanc Действительно, я не считаю Solaris <10 современным. Solaris 9 почти так же стар, как Windows XP!
Жиль "ТАК - перестань быть злым"
Жиль: бывают дни, когда я испытываю трудности с рассмотрением современного Solaris 10, когда Solaris 11
просуществовал
10

Вместо передачи пароля напрямую через аргумент или переменную окружения

#!/bin/bash
#filename: passwd_receiver
echo "The password is: $1"

используйте тот же аргумент или переменную среды для передачи имени файла :

#!/bin/bash
#filename: passwd_receiver
echo "The password is: $(< "$1")"

Затем вы можете передать либо разрешение защищенного обычного файла (хотя это не защитит вас от других процессов , работающих под тем же пользователем), или /dev/stdinи труба его в (который AFAIK защитит вас от других процессов , запущенных под тем же пользователем):

 echo PASSWORD | ./passwd_receiver /dev/stdin 

Если вы используете /dev/stdinздесь, обязательно, что это труба . Если это терминал, он будет доступен для чтения другим процессам, работающим под тем же пользователем.

Если вам уже нужно использовать ваш /dev/stdinдля чего-то другого, вы можете использовать подстановку процесса, если вы находитесь в оболочке, которая его поддерживает, что по сути эквивалентно использованию каналов:

./passwd_receiver <(echo PASSWORD)

Именованные каналы (FIFO) могут выглядеть так же, как они, но они приемлемы.

Эти решения также не совсем безопасны , но они могут быть достаточно близки, если вы не находитесь в системе с ограниченным объемом памяти, которая сильно меняется.

В идеале вы должны читать эти файлы (pipe - это тоже файл) в память, помеченную mlock (2) как не подлежащую удалению, что обычно делают такие программы обработки паролей, как gnupg.

Примечания:

  1. Передача номеров файловых дескрипторов теоретически так же хороша, как и файл, как передача имен файлов, но имена файлов более практичны, потому что <()дает вам имя файла, а не номер дескриптора файла (и coprocs дает вам файловые дескрипторы , помеченные как FD_CLOEXEC , что делает эти файловые дескрипторы непригодными в этом контексте).

  2. Если вы работаете в системе Linux с
    /proc/sys/kernel/yama/ptrace_scopeустановленным значением 0, тогда AFAIK, нет никакого пуленепробиваемого способа защитить себя от других процессов, запущенных под тем же пользователем (они могут использовать ptrace для подключения к вашему процессу и чтения вашей памяти)

  3. Если вам нужно только сохранить свой пароль от процессов, запущенных под разными (не-root) пользователями, тогда подойдут аргументы, переменные среды, каналы и файлы, защищенные разрешениями.

PSkocik
источник
7

Нет, переменные окружения тоже легко читаются и попадают в дочерние процессы. передать его с помощью трубы.

Jasen
источник
2
«переменные среды ... утечка в дочерние процессы». В этом весь смысл использования переменной среды. Они были бы бесполезны, если бы они не были унаследованы. «Переменные среды тоже легко читаются», нет.
Патрик
2
Прочитайте переменную, сбросьте ее. Это не сложно. И вы могли бы использовать тот же аргумент о трубе. Если канал не читается, он передается дочернему процессу, и дочерний процесс может прочитать его и получить пароль.
Патрик
1
@Patrick Документально средства неустановленного переменных окружения не обязательно вычистить значение от места , где psи /procможно увидеть.
zwol
1
Позволяет ли какая-либо система считывать переменные окружения произвольных процессов? Я не думаю, что Linux допускает это для процессов, принадлежащих другим, и если вы один и тот же пользователь, вы все равно можете просто ptrace()указать цель и прочитать ее память.
ilkkachu
5
Этот ответ неверен. Переменные среды не могут быть прочитаны другими пользователями.
Жиль "ТАК - перестань быть злым"
1

Если больше ничего не подходит, рассмотрите службу хранения ключей Linux (цепочки ключей ядра).

Начните с: security / keys.txt . Одна из цепочек ключей по умолчанию может быть клонирована между родительским и дочерним процессами.

Это не самое простое решение, но оно существует и, похоже, поддерживается и используется (оно также было связано с ошибкой Android в прошлом году).

Я не знаю о его «политическом» статусе, но у меня была аналогичная потребность, и я начал работать над привязкой Guile. Не сталкивался с уже существующей поддержкой Perl.

kzurell
источник