对于任何命令,有没有办法使用提示在 bash 中传递敏感数据?

cem*_*ate 50 shell security password

假设我使用sha1pass在命令行上生成一些敏感密码的哈希值。我可以sha1pass mysecret用来生成一个散列,mysecret但这有一个mysecret现在在 bash 历史中的缺点。有没有办法在避免mysecret以纯文本显示的同时实现此命令的最终目标,也许是通过使用passwd-style 提示?

我也对将敏感数据传递给任何命令的通用方法感兴趣。当敏感数据作为参数(例如 in sha1pass)或在 STDIN 上传递给某个命令时,该方法会发生变化。

有没有办法做到这一点?


编辑:这个问题引起了很多关注,下面提供了几个很好的答案。总结如下:

  • 根据@Kusalananda 的回答,理想情况下永远不必将密码或机密作为实用程序的命令行参数提供。正如他所描述的,这在几个方面都很脆弱,应该使用设计更好的实用程序,该实用程序能够在 STDIN 上获取秘密输入
  • @vfbsilva 的回答描述了如何防止东西被存储在 bash 历史记录中
  • @Jonathan 的回答描述了一个非常好的方法来完成这个,只要程序可以在 STDIN 上获取它的秘密数据。因此,我决定接受这个答案。sha1pass在我的 OP 中只是一个例子,但讨论已经确定存在更好的工具来获取 STDIN 上的数据。
  • @R ..在笔记他的回答,对一个变量使用命令扩展是不是安全的。

所以,总而言之,我接受了@Jonathan 的回答,因为这是最好的解决方案,因为您有一个设计良好且行为良好的程序可供使用。尽管将密码或机密作为命令行参数传递从根本上说是不安全的,但其他答案提供了减轻简单安全问题的方法。

Kus*_*nda 40

理想情况下,您永远不要在命令行上键入明文密码作为命令的参数。这样做会使密码成为命令的参数,并且可以通过使用简单的工具(例如ps或登录到某些审计日志)在进程表中看到命令行参数。

话虽如此,当然有办法从 shell 的命令历史中隐藏实际密码。

sha1pass "$( head -n 1 )"
Run Code Online (Sandbox Code Playgroud)

然后输入密码并按Enterhead此处使用的命令仅接受一行输入,并且您键入的最后一个换行符将不是传递给 的数据的一部分sha1pass

为了防止字符回显:

sha1pass "$( stty -echo; head -n 1; stty echo )"
Run Code Online (Sandbox Code Playgroud)

stty -echo命令关闭终端上键入的字符的回显。然后用 恢复回声stty echo

要传递标准输入,可以更改最后一个命令(如果sha1pass接受标准输入上的数据,您会这​​样做,但似乎此特定实用程序忽略了其标准输入):

{ stty -echo; head -n 1; stty echo; } | somecommand
Run Code Online (Sandbox Code Playgroud)

如果你需要多行输入(上述假设一行应传递,并在最后没有换行符),则更换整个head使用命令cat并终止输入(假设somecommand本身读取,直到结束文件)与Ctrl+DReturn如果您想在输入中包含换行符,请跟随,否则两次)。

无论您使用什么外壳,这都可以工作(只要它是类似 Bourne 或类似 rc 的外壳)。

如果命令前面有一个空格,则某些shell 可能不会将键入的命令保存在其历史文件中。这通常涉及必须设置HISTCONTROL为 value ignorespace。这至少在 OpenBSD 上支持bashksh,但不支持 egksh93dashzsh用户可以使用histignorespace选项或其HISTORY_IGNORE变量来定义要忽略的模式。

在支持读取read而不向终端回显字符的shell 中,您还可以使用

IFS= read -rs password     # -s turns off echoing in bash or zsh
                           # -r for reading backslashes as-is,
                           # IFS= to preserve leading and trailing blanks
sha1pass "$password"
Run Code Online (Sandbox Code Playgroud)

但这显然仍然存在与可能在进程表中泄露密码相同的问题。

如果实用程序从标准输入读取,并且 shell 支持“here-strings”,则上述内容可以更改为

IFS= read -rs password
somecommand <<<"$password"
Run Code Online (Sandbox Code Playgroud)

评论摘要如下:

  • 使用命令行上给出的密码执行命令,除了将数据通过管道传输到命令的命令外,上述所有命令都执行该命令,可能会使ps同时运行的任何人都可以看到密码。但是,如果从交互式 shell 执行,上述命令都不会将键入的密码保存在 shell 的历史文件中。

  • 读取明文密码的行为良好的程序通过从其标准输入、文件或直接从终端读取来实现。

  • sha1pass 确实需要在命令行上输入密码,直接输入或使用某种形式的命令替换。

  • 如果可能,请使用其他工具。

  • 这是不正确的。命令扩展 `$(...)` 的结果将是命令行的一部分,并且在 `ps` 输出中可见,除非在带有强化 /proc 的系统上。 (11认同)
  • 从安全的角度来看,这个答案是不安全的,唯一的好处是密码不会出现在 shell 历史记录中,但是通过在命令前面包含一个空格,这个优势更容易实现 (8认同)
  • @Kusalananda,没有设计良好的散列密码程序需要在命令行上输入,因此条件基本上是给定的——*如果不是*,则应选择不同的工具。 (5认同)
  • @CharlesDuffy:这里的工具似乎从根本上就坏了。`sha1pass` 在命令行上不带密码运行似乎会在不读取任何内容的情况下产生输出......因此 OP 需要选择不同的工具。 (4认同)
  • @Kusalananda,即使在开始后覆盖 argv 也会在覆盖发生之前留下一个易受攻击的窗口;这是一种缓解,而不是修复。 (3认同)

vfb*_*lva 26

如果你这样设置HISTCONTROL

HISTCONTROL=ignorespace
Run Code Online (Sandbox Code Playgroud)

并以空格开始命令:

~$  mycommand
Run Code Online (Sandbox Code Playgroud)

它不会存储在历史记录中。


小智 26

如果使用zshor bashshell,请使用readshell 内置选项的 -s 选项从终端设备读取一行而不回显它。

IFS= read -rs VARIABLE < /dev/tty
Run Code Online (Sandbox Code Playgroud)

然后你可以使用一些奇特的重定向来将变量用作标准输入。

sha1pass <<<"$VARIABLE"
Run Code Online (Sandbox Code Playgroud)

如果有人运行ps,他们将看到的只是“sha1pass”。

这假设sha1pass在没有给出任何参数时从 stdin 读取密码(在一行上,忽略行分隔符)。


R..*_*ICE 10

通过管道或 here-doc 传递敏感数据:

command_with_secret_output | command_with_secret_input
Run Code Online (Sandbox Code Playgroud)

或者:

command_with_secret_input <<EOF
$secret
EOF
Run Code Online (Sandbox Code Playgroud)

秘密位于(非导出的)shell 变量中是可以的,但是您永远不能在命令行上使用这些变量,只能在此处的文档和 shell 内部使用。

正如 Kusalananda 在评论中指出的那样,如果您在交互式 shell 中输入命令,您为 here 文档输入的行将存储在 shell 历史记录中,因此在那里输入密码是不安全的,但它仍然应该是安全地使用包含秘密的 shell 变量;历史将包含文本$secret而不是$secret扩展到的任何内容。

使用命令扩展是不安全的

command_with_secret_input "$(command_with_secret_output)"
Run Code Online (Sandbox Code Playgroud)

因为输出将包含在命令行中并在ps输出中可见(或手动从 /proc 读取),但在具有强化 /proc 的系统上除外。

给变量赋值也是可以的:

secret=$(command_with_secret_output)
Run Code Online (Sandbox Code Playgroud)

  • @Kusalananda:`sha1pass` 似乎从根本上损坏/不可用/不安全,因为它无法从标准输入或文件中读取密码。我认为需要一个不同的实用程序来解决这个问题。 (3认同)

ter*_*don 6

只需将值写入文件并传递文件:

$ cat > mysecret
Big seecreeeet!
$ cat mysecret | sha1pass 
Run Code Online (Sandbox Code Playgroud)

我不确定sha1pass它是如何工作的,如果它可以将文件作为输入,则可以使用sha1pass < mysecret. 如果没有,使用cat可能会出现问题,因为它包含最后的换行符。如果是这种情况,请使用(如果您head支持-c):

head -c-1 mysecret | sha1pass 
Run Code Online (Sandbox Code Playgroud)

  • 为什么在第一个示例中将密码写入磁盘,而您只需执行`cat | sha1pass`?`猫我的秘密| sha1pass` 和 `sha1pass &lt; mysecret` 对于最终换行符具有相同的行为。`cat` 不添加换行符。无论如何,如果`sha1pass` 支持通过标准输入传递密码,我希望它自己忽略最后的换行符。毕竟,带有未以换行符终止的行的文件在 unix 中是不自然的,因此期望文件未以换行符终止是很尴尬的。 (2认同)