让用户在 bash 上输入的密码成为程序标准输入的一部分的最安全和最简单的方法是什么?

Jim*_*and 14 security bash password curl

我正在寻找 (1) 最安全和 (2) 最简单的方法,让用户在 bash shell 提示符下键入密码,并使该密码成为程序标准输入的一部分。

这就是 stdin 需要的样子:{"username":"myname","password":"<my-password>"},在<my-password>shell 提示中输入的内容在哪里。如果我可以控制程序标准输入,那么我可以修改它以安全地提示输入密码并将其放置到位,但下游是标准的通用命令。

我已经考虑并拒绝了使用以下方法的方法:

  • 用户在命令行中输入密码:密码将显示在屏幕上,所有用户也可以通过“ps”看到
  • shell 变量插入到外部程序的参数中(例如,...$PASSWORD...):所有用户仍然可以通过“ps”看到密码
  • 环境变量(如果它们留在环境中):所有子进程都可以看到密码;如果作为诊断的一部分转储核心或转储环境变量,即使是值得信赖的进程也可能暴露密码
  • 密码长时间保存在文件中,即使是权限严格的文件:用户可能会意外暴露密码,root 用户可能会意外看到密码

我将把我当前的解决方案作为下面的答案,但如果有人提出一个更好的答案,我会很高兴地选择一个更好的答案。我认为应该有一些更简单的东西,或者也许有人看到了我遗漏的安全问题。

Sté*_*las 16

bashzsh

unset -v password # make sure it's not exported
set +o allexport  # make sure variables are not automatically exported
IFS= read -rs password < /dev/tty &&
  printf '{"username":"myname","password":"%s"}\n' "$password" | cmd
Run Code Online (Sandbox Code Playgroud)

如果没有IFS=read则会从您键入的密码中去除前导和尾随空格。

如果没有-r,它会将反斜杠作为引用字符处理。

你想确保你只从终端读取。

echo不能可靠地使用。在bashand 中zshprintf是内置的,因此命令行不会显示在ps.

在 中bash,您需要引用$password,否则将split+glob运算符应用于它。

但这仍然是错误的,因为您需要将该字符串编码为 JSON。例如,双引号和反斜杠至少会是一个问题。您可能需要担心这些字符的编码。您的程序是否需要 UTF-8 字符串?你的终端发送什么?

要添加提示字符串,请使用zsh

IFS= read -rs 'password?Please enter a password: '
Run Code Online (Sandbox Code Playgroud)

bash

IFS= read -rsp 'Please enter a password: ' password
Run Code Online (Sandbox Code Playgroud)


Jim*_*and 5

这是我的解决方案(已经过测试):

$ read -s PASSWORD
<user types password>
$ echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"'
Run Code Online (Sandbox Code Playgroud)

解释:

  1. read -s读取一行标准输入(密码),而不将该行回显到屏幕上。它存储在 shell 变量 PASSWORD 中。
  2. echo -n $PASSWORD将密码放在标准输出上,不带任何换行符。(echo 是 shell 内置命令,因此不会创建新进程,因此(AFAIK)作为 echo 参数的密码不会显示在 ps 上。)
  3. perl 被调用并从 stdin 读取密码$_
  4. perl 将密码放入$_全文中并将其打印到 stdout

如果这是一个 HTTPS POST,第二行将类似于:

echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"' | curl -H "Content-Type: application/json" -d@- -X POST <url-to-post-to>
Run Code Online (Sandbox Code Playgroud)

  • 这看起来不错。我只是注意到根本没有理由调用 perl;您可以完美地执行类似 `printf '{"username":"myname","password":"%s"}' $PASSWORD` 之类的操作,它保留相同的安全属性并为您节省外部进程。 (3认同)
  • 如果您想将解决方案推广到不支持 -s 标志的“read”命令,您可以使用“stty -echo; read PASSWORD; stty echo” (2认同)
  • 管道启动两个新进程,每一侧一个。使用此处字符串:`perl -e '...' &lt;&lt;&lt; "$PASSWORD"`。 (2认同)
  • @chepner,“bash”中的“&lt;&lt;&lt;”将内容存储在临时文件中,这更糟糕。 (2认同)
  • 根据 TLDP,它确实创建了一个文件,但任何其他进程都无法读取该文件。“此处的文档会创建临时文件,但这些文件在打开后将被删除,并且任何其他进程都无法访问。” http://tldp.org/LDP/abs/html/here-docs.html 就在示例 19-12 之后。 (2认同)