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


40

假设我sha1pass用来在命令行上生成一些敏感密码的哈希。我可以sha1pass mysecret用来生成的哈希,mysecret但这具有mysecretbash历史中现在存在的缺点。有没有一种方法可以完成此命令的最终目标,同时又可以避免mysecret使用passwd-style提示符显示纯文本格式?

我也对将敏感数据传递到任何命令的通用方法感兴趣。当敏感数据作为参数(例如in中sha1pass)或在STDIN上传递给某些命令时,该方法将更改。

有没有办法做到这一点?


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

  • 按照@Kusalananda的回答,理想情况下,永远不必将密码或机密信息作为实用程序的命令行参数提供。正如他所描述的那样,这很容易受到攻击,并且应该使用一种设计更好的实用程序,该实用程序能够接收STDIN上的秘密输入。
  • @vfbsilva的答案描述了如何防止事物存储在bash历史记录中
  • @Jonathan的答案描述了一种非常好的方法,只要程序可以在STDIN上获取其秘密数据即可。因此,我决定接受这个答案。sha1pass在我的OP中只是一个例子,但是讨论确定存在更好的工具,它们确实可以在STDIN上获取数据。
  • 正如@R ..他的答案中指出的那样,对变量使用命令扩展并不安全。

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


6
不仅如此:同一台计算机上具有列出正在运行的进程的权限的任何人都可能看到正在运行的进程sha1pass mysecret,因此知道是什么mysecret。(当然,这仅在程序实际运行时才起作用几秒钟……)
MathematicalOrchid

@MathematicalOrchid通过在私有虚拟机中运行可以避免这种情况。但是,要设置它仅生成一个密码可能太麻烦了……:-)
Kusalananda

5
@Kusalananda我的意思是,“即使您知道如何关闭命令历史记录,也不要在命令行上放置敏感数据” ...
MathematicalOrchid

2
可以知道,自从有很多年以来,SHA-1不再使用密钥或密码散列。
大卫·佛斯特

2
请注意,如果系统正在运行审核守护程序,则所有用户的所有命令和所有参数均由root集中记录,因此可以访问这些日志的任何人都将看到此日志。
Randall

Answers:


20

如果使用zshbashshell,请对read内置的shell 使用-s选项以从终端设备读取一行而不回显该行。

IFS= read -rs VARIABLE < /dev/tty

然后,您可以使用一些特殊的重定向来将该变量用作stdin。

sha1pass <<<"$VARIABLE"

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

假定sha1pass在未提供任何参数的情况下从stdin(在一行中,忽略行定界符)读取密码。


我相信我们建立了sha1pass不使用其标准输入流的地方。
Kusalananda

@Kusalananda好的,但是此方法适用于从stdin读取的程序。
乔纳森

因此,假设该实用程序可以在STDIN上进行秘密输入,那么这是一个安全可靠的可靠解决方案吗?

考虑到您有一个设计良好的程序可以使用,这是最好的解决方案,因此我决定接受此答案。sha1pass只是我的OP中的一个示例,显然不是最佳选择。

1
@cemulate,... 在命令行上不安全。在heredoc或herestring的上下文中(如此处的答案所示),它不会暴露给ps,但可能会写入到临时文件中-如果要避免这种情况,则sshpass < <(printf '%s\n' "$VARIABLE")可以考虑使用(因为printf是内置的,而不是外部命令,它不会传递给),也不能通过进行execv访问ps
查尔斯·达菲

35

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

话虽这么说,但是肯定有一些方法可以从Shell的命令历史中隐藏实际的密码。

sha1pass "$( head -n 1 )"

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

为防止字符回显:

sha1pass "$( stty -echo; head -n 1; stty echo )"

stty -echo命令关闭终端上键入字符的回显。然后使用还原回显stty echo

要传递标准输入,可以更改最后一个命令(如果sha1pass在标准输入上接受了数据,则可以执行此操作,但是看起来好像该特定实用程序正在忽略其标准输入):

{ stty -echo; head -n 1; stty echo; } | somecommand

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

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

如果某些外壳程序在命令的前面加上空格,则可以使键入的命令不保存在其历史记录文件中。这通常涉及必须设置HISTCONTROL为value ignorespace。这是由至少支持bashkshOpenBSD上,而不是由如ksh93dashzsh用户可以使用该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"

但这显然仍然存在潜在的问题,可能会在进程表中显示密码。

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

IFS= read -rs password
somecommand <<<"$password"

以下评论摘要:

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

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

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

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


11
这是不正确的。命令扩展的结果$(...)将成为命令行的一部分,并且在ps输出中可见,除非在具有强化/ proc的系统上。
R.,

3
@Kusalananda,甚至在您启动后覆盖argv时,都会在覆盖发生之前留下易受攻击的窗口;这是缓解措施,而不是解决方案。
查尔斯·达菲

5
@Kusalananda,没有经过精心设计的用于散列密码的程序将需要在命令行上输入,因此条件是基本给定的;如果不是,则应该选择另一种工具。
查尔斯·达菲

4
@CharlesDuffy:这里的工具似乎从根本上被破坏了。sha1pass在命令行上以无密码方式运行似乎会产生不读取任何内容的输出...因此OP需要选择其他工具。
R.,

8
从安全的角度来看,这个答案是不安全的,唯一的好处是密码不会显示在shell历史记录中,但是通过在命令前面添加空格可以更轻松地实现这一好处
Ferrybig

25

如果您这样设置HISTCONTROL

HISTCONTROL=ignorespace

并用空格启动命令:

~$  mycommand

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


10

通过管道或此处文档传递敏感数据:

command_with_secret_output | command_with_secret_input

要么:

command_with_secret_input <<EOF
$secret
EOF

将机密放在(未导出的)shell变量中很好,但是您永远不能在命令行上使用这些变量,只能在he​​re-documents和shell内部使用。

正如Kusalananda在评论中指出的那样,如果您要在交互式外壳程序中输入命令,则在此处文档中输入的行将存储在外壳程序历史记录中,因此在此处输入密码并不安全,但仍然应该安全使用包含机密的shell变量;历史记录将包含文本,$secret而不是$secret扩展的内容。

使用命令扩展并不安全

command_with_secret_input "$(command_with_secret_output)"

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

也可以为变量赋值:

secret=$(command_with_secret_output)

我正在寻找的是一个实际的命令command_with_secret_output,可以让我键入秘密输出。是否存在可以在此处替换的命令?

请注意,在交互式bash 会话中键入here-document 会将文档保存在历史记录文件中。
Kusalananda

@cemulate:只需使用内置的shell read,例如read -r secret。然后你的秘密就进入了$secret。如果愿意,您可以在其之前/之后运行stty以隐藏输入。
R.,

3
@Kusalananda:sha1pass似乎从根本上来说是损坏/无法使用/不安全,因为它无法从stdin或文件中读取密码。我认为,需要另一种实用程序来解决该问题。
R.,

1
@cemulate您对R的最后评论就在现场。这就是为什么它无济于事的原因。read -rs将完成这项工作(如果要包含-r密码,其中包含反斜杠),但您又回到了可能在流程列表中炫耀密码的情况(并取决于是否启用了流程记帐以及如何配置) ,同时还需要记帐日志)。
Kusalananda

6

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

$ cat > mysecret
Big seecreeeet!
$ cat mysecret | sha1pass 

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

head -c-1 mysecret | sha1pass 

2
如果可以,为什么在第一个示例中将密码写入磁盘cat | sha1passcat mysecret | sha1pass并且sha1pass < mysecret对于最终换行符具有相同的行为。cat不添加换行符。无论如何,如果sha1pass支持通过标准输入传递密码,我希望它本身会忽略最后的换行符。毕竟行中没有被换行符终止的文件在unix中是不自然的,因此期望文件没有被换行符终止是很尴尬的。
JoL

@JoL我),因为我没有想到这一点://但那将如何工作?cat | sha1pass似乎sha1pass在让我有机会输入任何输入之前运行。ii)不,当然cat mysecret不会添加换行符,我从未说过要cat添加换行符,只是要包含换行符。
terdon

我知道了,但这也不像< mysecret将其删除。:)
JoL

没关系。现在,我重新阅读,我看到您说的是稍后提出head -c-1。我想我只是对为什么使用感到困惑cat mysecret | sha1pass,然后提出了建议sha1pass < mysecret。我猜担心的是sha1pass可能会抱怨stdin的常规文件。我不知道为什么。
光明审判

@JoL的原因更多是因为我在搜索中花了几分钟才从未使用过sha1pass,也找不到手册或-h任何其他形式的文档,因此无法弄清楚是否sha1pass foo将其foo视为输入字符串或输入文件运行。因此,我给出了应对每种可能性的选择。
terdon

1

如果terdon做了什么,那么通过标准输入,那是最好的解决方案。剩下的唯一问题是他将密码写入了磁盘。我们可以改为:

stty -echo
echo -n "password: "
head -1 | sha1pass
stty echo

就像Kusalananda所说的那样,stty -echo确保您键入的内容在stty echo再次执行之前不会显示。head -1将从标准输入中获取一行,并将其传递给sha1pass


0

我会用

sha1pass "$(cat)"

cat会从stdin读取直到EOF,这可能是由于按Ctrl + D引起的。然后,结果将作为参数传递给sha1pass


3
R ..的答案解释了为什么这是不安全的。
hvd18年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.