O que é mais seguro e a maneira mais simples de ter uma senha digitada pelo usuário no bash se torna parte de stdin para um programa?

11

Estou procurando a (1) maneira mais segura e (2) mais simples de fazer um usuário digitar uma senha em um prompt bash e fazer com que essa senha se torne parte de stdin para um programa.

Isto é o que o stdin precisa ter: {"username":"myname","password":"<my-password>"} , onde <my-password> é o que foi digitado no prompt do shell. Se eu tivesse controle sobre o programa stdin, então eu poderia modificá-lo para solicitar uma senha com segurança e colocá-la no lugar, mas o downstream é um comando padrão de propósito geral.

Eu considerei e rejeitei abordagens que usam o seguinte:

  • o usuário digitando a senha na linha de comando: a senha seria mostrado na tela e também visível para todos os usuários via "ps"
  • interpolação de variável de shell em um argumento para um programa externo (por exemplo, ...$PASSWORD... ): a senha ainda seria visível para todos usuários via "ps"
  • variáveis de ambiente (se forem deixadas no ambiente): a senha seria visível para todos os processos filhos; mesmo confiável processos podem expor a senha se eles despejarem o núcleo ou o dump variáveis de ambiente como parte de um diagnóstico
  • a senha em um arquivo por um longo período de tempo, até mesmo um arquivo com permissões restritas: o usuário pode expor acidentalmente a senha e o usuário root pode acidentalmente ver a senha

Eu colocarei minha solução atual como uma resposta abaixo, mas, felizmente, selecionarei uma resposta melhor se alguém apresentar uma. Estou pensando que deveria haver algo mais simples ou talvez alguém veja uma preocupação de segurança que eu perdi.

    
por Jim Hoagland 25.09.2015 / 18:02

3 respostas

10

com bash ou 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

Sem IFS= , read removeria os espaços em branco iniciais e finais da senha digitada.

Sem -r , ele processaria as barras invertidas como um caractere de citação.

Você quer ter certeza de que só lê o terminal.

echo não pode ser usado de forma confiável. Em bash e zsh , printf está embutido para que a linha de comando não seja mostrada na saída de ps .

Em bash , você precisa citar $password , caso contrário, o operador split + glob será aplicado a ele.

Isso ainda está errado, já que você precisa codificar essa string como JSON. Por exemplo, aspas duplas e barras invertidas, pelo menos, seriam um problema. Você provavelmente precisa se preocupar com a codificação desses caracteres. Seu programa espera seqüências de caracteres UTF-8? O que o seu terminal envia?

Para adicionar uma string de prompt, com zsh :

IFS= read -rs 'password?Please enter a password: '

com bash :

IFS= read -rsp 'Please enter a password: ' password
    
por 25.09.2015 / 19:03
1

Aqui está a solução que eu tenho (foi testada):

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

Explicação:

  1. read -s lê uma linha de stdin (a senha) sem ecoar a linha na tela. Ele armazena está na variável shell PASSWORD.
  2. echo -n $PASSWORD coloca a senha no stdout sem nenhuma nova linha. (echo é um comando interno do shell, portanto, nenhum novo processo é criado, portanto, (AFAIK) a senha como um argumento para echo não será mostrada no ps.)
  3. perl é invocado e lê a senha de stdin para $_
  4. perl coloca a senha em $_ no texto completo e a imprime para stdout

Se isso for para um HTTPS POST, a segunda linha seria algo como:

echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"' | curl -H "Content-Type: application/json" -d@- -X POST <url-to-post-to>
    
por 25.09.2015 / 18:09
0

Se a sua versão de read não for compatível com -s , experimente a maneira POSIX compatível vista em esta resposta .

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

O set +a deve impedir a "exportação" automática de uma variável para o ambiente. Você deve verificar as man pages para stty , existem muitas opções disponíveis. O <<<"$passwd" é citado porque boas senhas podem ter espaços. O último echo após ativar o stty echo é ter o próximo comando / saída iniciado em uma nova linha.

    
por 22.07.2017 / 00:48