как избежать записи неудачных команд bash в bash_history

Давайте разберем:

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 
4
29.05.2019, 15:31
1 ответ

Попробуйте это:

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, чтобы сделать его постоянным.

1
05.02.2021, 11:45

Теги

Похожие вопросы