Maneira segura de passar a senha para> 1 programas no bash

21

Estou escrevendo um bashscript e preciso solicitar a senha do usuário e passá-la para openssl. Embora opensslpossa ler a senha em si, preciso de duas execuções do programa e não quero perguntar ao usuário duas vezes. Aqui está o script:

cp file{,.old}
read -sp 'Enter password. ' PASS; echo
export PASS

# decode | edit | encode
openssl enc -d -aes-256-cbc -k "$PASS" -in file.old | \
  sed ... | openssl enc -e -aes-256-cbc -k "$PASS" -out file

unset PASS

Isso não é seguro, pois a senha está facilmente disponível olhando a linha de comando; alguém pode lê-lo usando ps, por exemplo.

opensslpode ler uma senha de uma variável de ambiente, para que eu possa substituir -k "$PASS"por -pass env:PASS, mas ainda não é seguro; as variáveis ​​de ambiente de qualquer processo podem ser lidas livremente (novamente, é pspossível).

Então, como posso passar com segurança a senha para as duas opensslinstâncias?

Chris Down
fonte
combinação de GnuPG e pinentry podem ser usados aqui gnupg.org/related_software/pinentry/index.en.html
Nikhil Mulley
"as variáveis ​​de ambiente de qualquer processo podem ser lidas livremente" - isso não está totalmente correto. pslê o ambiente de um processo /proc/<pid>/environ, mas esse arquivo tem 0600permissões; portanto, apenas o usuário root e o usuário que está executando o processo podem ler o ambiente do processo. Eu diria que é bem seguro.
Martin von Wittich

Respostas:

16

Passe a senha em um descritor de arquivo separado da entrada (duas vezes, uma para criptografia e outra para descriptografia). Não exporte PASSpara o meio ambiente.

read -sp 'Enter password. ' PASS
printf '%s\n' "$PASS" |
openssl enc -d -aes-256-cbc -kfile /dev/stdin -in file.old |
sed ... | {
  printf '%s\n' "$PASS" |
  openssl enc -e -aes-256-cbc -kfile /dev/stdin -in /dev/fd/3 -out file;
} 3<&0

Se o seu sistema não possui /dev/fd, você pode usar o -passargumento para dizer opensslpara ler a senha de um descritor de arquivo aberto.

printf '%s\n' "$PASS" | {
  printf '%s\n' "$PASS" |
  openssl enc -d -aes-256-cbc -pass fd:0 -in file.old |
  tr a-z A-Z | tee /dev/tty | {
  openssl enc -e -aes-256-cbc -pass fd:3 -out file; }
} 3<&0
Gilles 'SO- parar de ser mau'
fonte
Pelo que entendi de outra sua resposta , na bashversão com também env:PASSé seguro.
printf '%s\n' "$PASS"não é seguro. Alguém pode ler a linha de comando, pspor exemplo.
6
@ user14284 Não e não. env:PASSnão é seguro porque a senha apareceria no ambiente do opensslprocesso (não apareceria no ambiente do bashprocesso, mas isso não é suficiente). O uso printfé seguro porque é um bash embutido.
Gilles 'SO- stop be evil'
echo é um bash embutido; portanto, um simples comando echo não seria seguro? echo $PASS | openssl .... Não apareceria na lista ps. O único lugar que você pode obter aprovação seria na memória do processo do bash. Eu acho que ?
gaoithe
11
@gaoithe Sim, echoseria seguro pelo mesmo motivo que printfé seguro (e printfnão seria seguro em uma concha onde não está embutido). A razão pela qual eu uso printfe não echoé que isso echopode causar barras invertidas (dependendo das opções do bash).
Gilles 'SO- deixa de ser mau'
8

Usando o Bash, isso pode ser feito sem o uso printf '%s\n' "$PASS"da associação da chamada string aqui aos descritores de arquivo, usando o execcomando Bash builtin .

Para obter mais informações, consulte: Segurança da senha do script do shell dos parâmetros da linha de comandos .

(

# sample code to edit password-protected file with openssl
# user should have to enter password only once
# password should not become visible using the ps command

echo hello > tmp.file

#env -i bash --norc   # clean up environment
set +o history
unset PASS || exit 1

read -sp 'Enter password. ' PASS; echo

# encrypt file and protect it by given password
exec 3<<<"$PASS"
openssl enc -e -aes-256-cbc -pass fd:3  -in tmp.file -out file

cp file{,.old}

# decode | edit | encode
exec 3<<<"$PASS" 4<<<"$PASS"
openssl enc -d -aes-256-cbc -pass fd:3 -in file.old | 
   sed 's/l/L/g' | 
   openssl enc -e -aes-256-cbc -pass fd:4 -out file

exec 3<<<"$PASS"
openssl enc -d -aes-256-cbc -pass fd:3 -in file

rm -P tmp.file file.old
unset PASS

)
Jon
fonte
1

Desculpe, minha resposta anterior foi do openssl man, e não do openssl enc docs.

Essa solução não é um pipeline, mas acredito que ela evite que a senha seja visível para o ps.

Usando um documento aqui, o openssl vê apenas o texto da senha.
Contanto que você elimine o arquivo intermediário, nenhum vestígio permanece. Talvez alguém possa ajudar a fazer isso em um pipeline e eliminar o arquivo intermediário?

# cp file{,.old}  don't need this anymore since intermediate becomes same
read -sp 'Enter password. ' PASS; echo
#no need to export, env's are readable, as mentioned

# decode into intermediate file
openssl <<HERE 2>&1 >/dev/null
enc -d -aes-256-cbc -k "$PASS" -in file -out intermediate
HERE

# edit intermediate

# encode intermediate back into file
openssl <<HERE 2>&1 >/dev/null
enc -e -aes-256-cbc -k "$PASS" -in intermediate -out file 
HERE
unset PASS
rm -f intermediate
bsd
fonte
Essa seria uma resposta melhor se explicasse como usar o switch. Não está errado (exceto que o enccomando não tem -knopção, pelo menos nas versões atuais -pass), mas não é muito informativo. (O voto negativo não é meu.)
Gilles 'pare de ser mau'
Obrigado @Gilles, olhou para os documentos e viu meu erro, resposta atualizada com uma abordagem diferente.
BSD