Como passar uma senha para um processo filho?

18

Passar uma senha na linha de comando (para um processo filho iniciado no meu programa) é conhecido por não ser seguro (porque pode ser visto até por outros usuários com o comando ps). É possível passar isso como uma variável de ambiente?

O que mais posso usar para passar isso? (Exceto a variável de ambiente), a solução mais fácil parece usar um canal, mas essa solução mais fácil não é fácil.

Eu programa em Perl.

Porton
fonte
2
Por que não é fácil? Ele não precisa ser um pipe separado / nomeado, apenas stdin / out regular fará ... isso não deve causar muitos problemas em qualquer idioma. Você pode colocá-lo em um arquivo de configuração simples se puder garantir que seja legível apenas por processos de interesse (o que é muito mais difícil do que parece).
Frostschutz
11
Se você não chamar exec no filho, ainda terá uma cópia da senha sem precisar fazer nada.
James Youngman

Respostas:

26

Os argumentos do processo são visíveis para todos os usuários, mas o ambiente é visível apenas para o mesmo usuário ( pelo menos no Linux , e acho que em todas as variantes modernas do unix). Portanto, passar uma senha por uma variável de ambiente é seguro. Se alguém puder ler suas variáveis ​​de ambiente, ele poderá executar processos como você, então já acabou o jogo.

O conteúdo do ambiente corre algum risco de vazar indiretamente, por exemplo, se você correr pspara investigar algo e colar e copiar acidentalmente o resultado, incluindo variáveis ​​de ambiente confidenciais em um local público. Outro risco é que você passa a variável de ambiente para um programa que não precisa dela (incluindo filhos do processo que precisa da senha) e esse programa expõe suas variáveis ​​de ambiente porque não espera que sejam confidenciais. A gravidade desses riscos de vazamento secundário depende de qual é o processo com a senha (por quanto tempo é executado? Executa subprocessos?).

É mais fácil garantir que a senha não vaze acidentalmente, passando-a por um canal que não foi projetado para ser espionado, como um cano. Isso é muito fácil de fazer no lado de envio. Por exemplo, se você tiver a senha em uma variável do shell, poderá fazer

echo "$password" | theprogram

se theprogramespera a senha em sua entrada padrão. Observe que isso é seguro porque echoé um builtin; não seria seguro com um comando externo, pois o argumento seria exposto na pssaída. Outra maneira de obter o mesmo efeito é com um documento aqui:

theprogram <<EOF
$password
EOF

Alguns programas que exigem uma senha podem ser instruídos para lê-la em um descritor de arquivo específico. Você pode usar um descritor de arquivo diferente da entrada padrão se precisar de entrada padrão para outra coisa. Por exemplo, com gpg:

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

Se o programa não pode ser instruído a ler a partir de um descritor de arquivo, mas pode ser instruído a ler a partir de um arquivo, você pode instruí-lo a ler a partir de um descritor de arquivo usando um nome de arquivo como `/ dev / fd / 3.

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

No ksh, bash ou zsh, você pode fazer isso de forma mais concisa através da substituição do processo.

theprogram --password-from-file=<(echo "$password")
Gilles 'SO- parar de ser mau'
fonte
No Solaris 9 e mais antigo, o /usr/ucb/pssetuid root era capaz de ler e exibir variáveis ​​de ambiente de outros processos - isso foi removido no Solaris 10; portanto, a resposta "todas as outras variantes modernas do Unix" acima se aplica às versões Solaris a partir de 2005 e posteriores.
Alanc
@alanc De fato, não considero o Solaris <10 moderno hoje em dia. O Solaris 9 é quase tão antigo quanto o Windows XP!
Gilles 'SO- stop be evil'
Gilles: há dias em que tenho dificuldade em considerar o Solaris 10 moderno agora que o Solaris 11 está fora há mais de 5 anos, então eu entendo e concordo completamente, mas, infelizmente, sei que algumas pessoas ainda
usam o
10

Em vez de passar a senha diretamente através de um argumento ou variável de ambiente

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

use o mesmo argumento ou variável de ambiente para passar um nome de arquivo :

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

Então você pode passar tanto um arquivo regular protegido por permissão (embora isso não vai protegê-lo de outros processos em execução sob o mesmo usuário), ou /dev/stdine tubulação -lo em (que AFAIK irá protegê-lo de outros processos em execução sob o mesmo usuário):

 echo PASSWORD | ./passwd_receiver /dev/stdin 

Se você usa /dev/stdinaqui, é imperativo que seja um cano . Se for um terminal, será legível por outros processos em execução no mesmo usuário.

Se você já precisar usar o seu /dev/stdinpara outra coisa, poderá usar a substituição de processo se estiver em um shell que o suporte, o que é essencialmente equivalente ao uso de pipes:

./passwd_receiver <(echo PASSWORD)

Os pipes nomeados (FIFOs) podem parecer iguais, mas são interceptáveis.

Essas soluções também não são perfeitamente seguras , mas podem estar próximas o suficiente, desde que você não esteja em um sistema com restrição de memória que troque muito.

Idealmente, você deve ler esses arquivos (pipe também é um arquivo) na memória marcada com mlock (2) como não trocável, que é o que geralmente fazem os programas de manipulação de senhas, como o gnupg.

Notas:

  1. Passar números de filedescriptor é, teoricamente, tão bom quanto arquivo como passar nomes de arquivos, mas os nomes de arquivos são mais práticos, porque <()fornece um nome de arquivo, não um número de filedescriptor ( coproces fornecem os descritores de arquivo marcados como FD_CLOEXEC , o que torna esses descritores de arquivo inutilizáveis ​​nesse contexto).

  2. Se você estiver em um sistema Linux
    /proc/sys/kernel/yama/ptrace_scopedefinido como 0AFAIK, não há maneira de se proteger de outros processos em execução no mesmo usuário (eles podem usar o ptrace para se conectar ao seu processo e ler sua memória)

  3. Se você apenas precisar manter sua senha longe de processos em execução sob usuários diferentes (não raiz), argumentos, variáveis ​​de ambiente, canais e arquivos protegidos por permissão serão suficientes.

PSkocik
fonte
7

Não, as variáveis ​​de ambiente também são facilmente lidas e vazam para os processos filhos. passe-o usando um cano.

Jasen
fonte
2
"variáveis ​​de ambiente ... vazam para processos filho" Esse é o objetivo de usar uma variável de ambiente. Eles seriam inúteis se não fossem herdados. "variáveis ​​de ambiente também são facilmente lidas", não, não são.
Patrick
2
Leia a variável, desative-a. Não é díficil. E você pode usar o mesmo argumento sobre o tubo. Se o canal não for lido, ele será passado para o processo filho, e o processo filho poderá lê-lo e obter a senha.
22416 Patrick Patrick
11
@Patrick Os meios documentados de variáveis de ambiente Destruindo não necessariamente esfregar o valor a partir do local onde pse /procpode vê-lo.
Zwol 16/07/19
11
Algum sistema permite ler variáveis ​​de ambiente de processos arbitrários? Não acho que o Linux permita processos pertencentes a outras pessoas e, se você for o mesmo usuário, poderá apenas ptrace()atingir o alvo e ler sua memória de qualquer maneira.
Ilkkachu
5
Esta resposta está errada. Variáveis ​​de ambiente não podem ser lidas por outros usuários.
Gilles 'SO- stop be evil'
1

Se nada mais servir, considere o serviço de Retenção de Chave do Linux (chaveiros do kernel).

Comece em: security / keys.txt . Um dos chaveiros padrão pode ser clonado entre os processos pai e filho.

Não é a solução mais simples, mas está lá e parece ser mantida e usada (também foi implicada em um bug do Android no ano passado).

Eu não sei sobre seu status "político", mas tinha uma necessidade semelhante e comecei a trabalhar em uma ligação do Guile. Não encontrei suporte Perl pré-existente.

kzurell
fonte