Давайте разберем:
content=$(cat -)
То же, что и content=$(cat)
. Это использует замену команды , которая в bash
использует канал. На одном конце канала cat
записывает то, что читает со своего стандартного ввода. А bash
на другом конце считывает это для сохранения в $content
.
Однако перед этим он удаляет завершающие символы новой строки, а также блокирует символы NUL. Чтобы иметь возможность хранить произвольные данные в переменной, вы не можете использовать bash
; вместо этого вам нужно будет использовать zsh
и сделать что-то вроде:
content=$(cat; echo.); content=${content%.}
, чтобы обойти проблему удаления новой строки.
Обратите внимание, что данные, которые должны быть сохранены в $content
, передаются через стандартный ввод вашего скрипта. Это будет доступно (в Linux )как /proc/pid-of-your-script/fd/0
, что совпадает с /proc/pid-of-cat/fd/0
. И так как cat
копирует его в канал в bash
, он также находится в /proc/pid-of-cat/fd/1
и /proc/pid-of-script/fd/fd-to-the-other-end-of-the-pipe
.
В любом случае здесь вы используете содержимое $content
только один раз, поэтому нет смысла делать этот промежуточный шаг.
pass=$1
Здесь вы говорите, что тот самый секретный ключ берется из первого аргумента скрипта.
Вы не можете передавать секретные данные в качестве аргументов командной строки. Аргументы командной строки не являются секретными. Они отображаются в выводе ps -efwww
. В Linux /proc/pid/cmdline
, который их содержит, по умолчанию доступен для чтения -всему миру (; на линуксе,доступ к /proc/pid
может быть административно ограничен процессами с тем же euid, хотя это делается редко, так как это влияет на поведение или ps
среди прочего ).
В некоторых системах они могут даже регистрироваться с помощью механизма учета/аудита процессов.
echo $content
неверно по нескольким причинам:
echo
нельзя использовать для произвольных данных. Это не работает должным образом с такими аргументами, как -n
, -neneneene
... и в зависимости от среды, которые содержат обратную косую черту. $content
без кавычек означает вызов оператора split+glob, который вам не нужен и может вызвать проблемы, если $content
содержит символы $IFS
или подстановочные знаки. Вы бы хотели:
printf %s "$content"
И убедитесь, что завершающие символы новой строки не были удалены ранее.
Поскольку этот printf
является частью конвейера, он будет запущен в дочернем процессе, чей стандартный вывод будет каналом. В Linux /proc/pid-of-that-child-process/fd/1
предоставит доступ к этому содержимому. Так будет /proc/pid-of-ccript/0
.
В
ccrypt -f -k <(echo -n $pass)
Опять проблема echo
и $pass
без кавычек.
echo
(снова запущенный в дочернем процессе )запишет пароль в канал. Другой конец канала будет доступен на fd n до ccrypt
и <(...)
расширится до чего-то вроде /dev/fd/n
или /proc/self/fd/n
. ccrypt
откроет этот файл (, поэтому на новом fd ), который снова (в Linux )будет доступен как /proc/pid-of-ccrypt/fd/that-fd
в дополнение к /proc/pid-of-ccrypt/fd/n
и/proc/pid-of-echo/fd/1
Основная проблема в вашем коде заключается не в подстановке процессов или других каналах, а в том, что пароль задается в качестве аргумента командной строки команды (здесь, вашего сценария ).
Подстановка процесса включает в себя обычный конвейер, как и при подстановке команд $(...)
и |
./dev/fd/x
в большинстве систем, кроме Linux, имеет значение только для соответствующего процесса, поэтому не может передаваться другим процессам. Но другие процессы, работающие с тем же euid (или с правами root ), в любом случае могут считывать память этих процессов (, как это делают отладчики ), и восстанавливать этот пароль (или, возможно, все равно получать его из того же источника. ).
В Linux /dev/fd
— это символическая ссылка на /proc/self/fd
, а /proc/self
— динамическая символическая ссылка на /proc/the-pid
. /proc/pid/fd
по умолчанию доступен для чтения процессам с тем же euid (, хотя могут быть добавлены дополнительные ограничения, те же самые, которые ограничивают, кто может подключать отладчик к процессу ).
Для fds, указывающих на каналы, /proc/pid/fd/that-fd
действует как именованный канал. Таким образом, другой процесс (, снова работающий с тем же euid или root ), может украсть содержимое канала. Но в любом случае, если они могут это сделать, они также могут напрямую читать содержимое памяти процессов, поэтому нет смысла пытаться защититься от этого.
Вместо передачи пароля в командной строке вы можете передать его через переменную среды. Среда намного более приватна, чем список аргументов. В Linux /proc/pid/environ
могут читать только процессы с одинаковым euid (или root
).
Таким образом, ваш сценарий может быть просто:
#! /bin/sh -
exec ccrypt -f -E PASSWORD
И назовите это как
PASSWORD=secret-phrase the-script < data-to-encrypt
Попробуйте это:
export PROMPT_COMMAND='LAST_COMMAND_EXIT=$? && history -a && test 127 -eq $LAST_COMMAND_EXIT && head -n -2 $HISTFILE >${HISTFILE}_temp && mv ${HISTFILE}_temp $HISTFILE'
Просто удаляет последние две строки (временную метку и команду )файла гистфайла, если статус выхода равен 127. У меня хорошо работает, если у вас нет метки времени в истории, вы должны использовать head -n -1
вместо-2
Конечно, вы должны добавить эту строку в ваш.bashrc, чтобы сделать его постоянным.