Vamos dividi-lo:
content=$(cat -)
Isso é o mesmo que content=$(cat)
. Isso é usando substituição de comando que em bash
usa um pipe. Em uma extremidade do pipe, cat
está escrevendo o que lê em seu stdin. E bash
na outra ponta está lendo isso para armazenar em $content
.
No entanto, antes de fazer isso, ele remove caracteres de nova linha e também sufocaria em caracteres NUL. Para poder armazenar dados arbitrários em uma variável, você não pode usar bash
; você precisaria usar zsh
e fazer algo como:
content=$(cat; echo .); content=${content%.}
para contornar o problema da remoção de novas linhas.
Observe que os dados a serem armazenados em $content
são alimentados por meio do stdin do seu script. Isso estaria disponível (no Linux) como /proc/pid-of-your-script/fd/0
, que é o mesmo que /proc/pid-of-cat/fd/0
. E como cat
copia para um canal para bash
, também está em /proc/pid-of-cat/fd/1
e /proc/pid-of-script/fd/fd-to-the-other-end-of-the-pipe
.
De qualquer forma, aqui, você está usando o conteúdo de $content
apenas uma vez, por isso não faz sentido fazer essa etapa intermediária.
pass=$1
Aqui, você está dizendo que a própria chave é retirada do primeiro argumento do script.
Você não pode passar dados secretos como argumentos de linha de comando. Argumentos de linha de comando não são secretos. Eles aparecem na saída de ps -efwww
. No Linux, /proc/pid/cmdline
que os contém é legível por todos (por padrão; no Linux, o acesso a /proc/pid
pode ser restrito administrativamente a processos com o mesmo euid, embora isso raramente afeta o comportamento ou ps
entre outros coisas).
Em alguns sistemas, eles podem até ser registrados por meio de algum mecanismo de contabilidade / auditoria de processos.
echo $content
está errado por vários motivos:
-
echo
não pode ser usado para dados arbitrários. Ele não funciona adequadamente com argumentos como -n
, -neneneene
... e dependendo do ambiente, coisas que contêm barras invertidas.
- Um
$content
sem aspas significa a chamada do operador split + glob que você não deseja e causaria problemas se $content
contiver caracteres de $IFS
ou curingas.
- adiciona um caractere extra de nova linha.
Você deseja:
printf %s "$content"
E certifique-se de que as novas linhas finais não foram removidas anteriormente.
Como printf
faz parte de um pipeline, ele será executado em um processo filho cujo stdout será um pipe. No Linux, /proc/pid-of-that-child-process/fd/1
dará acesso a esse conteúdo. Então vai /proc/pid-of-ccript/0
.
Em
ccrypt -f -k <(echo -n $pass)
Existe novamente o problema de echo
e o $pass
não referenciado.
echo
(novamente em execução em um processo filho) gravará a senha em um canal. A outra extremidade do canal será disponibilizada em fd n para ccrypt
e <(...)
expandirá para algo como /dev/fd/n
ou /proc/self/fd/n
. ccrypt
abrirá esse arquivo (assim em um novo fd), que novamente (no Linux) estará disponível como /proc/pid-of-ccrypt/fd/that-fd
além de /proc/pid-of-ccrypt/fd/n
e /proc/pid-of-echo/fd/1
Agora, o principal problema em seu código não é a substituição de processo ou qualquer um desses outros canais, mas o fato de que a senha é fornecida como um argumento de linha de comando de um comando (aqui, seu script).
A substituição do processo envolve um canal normal, assim como com a substituição do comando $(...)
e |
. /dev/fd/x
na maioria dos sistemas, exceto o Linux, só é significativo para o processo correspondente, portanto, não pode vazar para outros processos. Mas outros processos em execução como o mesmo euid (ou como root) poderiam ler a memória desses processos de qualquer maneira (como os depuradores fazem) e recuperar essa senha (ou provavelmente obtê-la da mesma fonte).
No Linux, /dev/fd
é um link simbólico para /proc/self/fd
e /proc/self
um link simbólico dinâmico para /proc/the-pid
. /proc/pid/fd
é por padrão legível por processos com o mesmo euid (embora possa haver outras restrições adicionadas, as mesmas que restringem quem pode anexar um depurador a um processo).
Para fds que apontam para pipes, /proc/pid/fd/that-fd
age como um pipe nomeado. Então, outro processo (novamente rodando como o mesmo euid ou root) poderia roubar o conteúdo do pipe. Mas, em qualquer caso, se eles podem fazer isso, eles também podem ler diretamente o conteúdo da memória do processo, então não adianta tentar se proteger contra isso.
Em vez de passar a senha em uma linha de comando, você poderia passá-la por meio de uma variável de ambiente. O ambiente é muito mais privado que a lista de argumentos. No Linux, /proc/pid/environ
só é legível por processos com o mesmo euid (ou root
).
Assim, o seu script pode ser apenas:
#! /bin/sh -
exec ccrypt -f -E PASSWORD
E chame como
PASSWORD=secret-phrase the-script < data-to-encrypt