流程替换中的敏感数据

流程替换中的敏感数据

假设我这样做:

#!/bin/bash
content="foo"
pass="secret-password"
echo $content | ccrypt -f -k <(echo -n $pass)  

是否可以信任在进程替换文件上设置的权限/dev/fd/*来保证密码在 的持续时间内安全ccrypt

答案1

让我们来分解一下:

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将其复制到管道 to 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包含它们的文件是世界可读的(默认情况下;在 Linux 上,/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 上可用ntoccrypt<(...)will 扩展为类似/dev/fd/nor 的东西/proc/self/fd/nccrypt将打开该文件(因此在新的 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 的进程可以读取(尽管可以添加进一步的限制,与限制谁可以将调试器附加到进程的限制相同)。

对于指向管道的 fd,/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 

相关内容