![让用户在 bash 上输入的密码成为程序标准输入的一部分的最安全和最简单的方法是什么?](https://linux22.com/image/73815/%E8%AE%A9%E7%94%A8%E6%88%B7%E5%9C%A8%20bash%20%E4%B8%8A%E8%BE%93%E5%85%A5%E7%9A%84%E5%AF%86%E7%A0%81%E6%88%90%E4%B8%BA%E7%A8%8B%E5%BA%8F%E6%A0%87%E5%87%86%E8%BE%93%E5%85%A5%E7%9A%84%E4%B8%80%E9%83%A8%E5%88%86%E7%9A%84%E6%9C%80%E5%AE%89%E5%85%A8%E5%92%8C%E6%9C%80%E7%AE%80%E5%8D%95%E7%9A%84%E6%96%B9%E6%B3%95%E6%98%AF%E4%BB%80%E4%B9%88%EF%BC%9F.png)
我正在寻找(1)最安全和(2)最简单的方法,让用户在 bash shell 提示符下键入密码,并使该密码成为程序标准输入的一部分。
这就是 stdin 需要的样子:{"username":"myname","password":"<my-password>"}
,其中<my-password>
是在 shell 提示符中输入的内容。如果我可以控制程序标准输入,那么我可以修改它以安全地提示输入密码并将其放置到位,但下游是标准的通用命令。
我已经考虑并拒绝了使用以下内容的方法:
- 用户在命令行中输入密码:密码将显示在屏幕上,并且所有用户也可以通过“ps”看到
- shell 变量插值到外部程序的参数中(例如
...$PASSWORD...
):密码仍然对所有用户通过“ps”可见 - 环境变量(如果它们留在环境中):密码对所有子进程都是可见的;即使是值得信赖的进程,如果在诊断过程中转储核心或转储环境变量,也可能会暴露密码
- 密码长时间保存在文件中,即使是权限严格的文件:用户可能会意外暴露密码,而 root 用户可能会意外看到密码
我将把我当前的解决方案作为下面的答案,但如果有人提出一个更好的答案,我会很乐意选择一个更好的答案。我认为应该有一些更简单的东西,或者也许有人看到了我错过的安全问题。
答案1
与bash
或zsh
:
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
如果没有IFS=
,read
将从您键入的密码中去除前导和尾随空格。
如果没有-r
,它将把反斜杠作为引用字符处理。
您要确保只从终端读取数据。
echo
无法可靠使用。在bash
和中zsh
,printf
是内置的,因此命令行不会显示在 的输出中ps
。
在 中bash
,您需要引用$password
,否则分割+全局运算符应用于它。
但这仍然是错误的,因为您需要将该字符串编码为 JSON。例如,双引号和反斜杠至少会是一个问题。您可能需要担心这些字符的编码。您的程序是否需要 UTF-8 字符串?你的终端发送什么?
要添加提示字符串,请使用zsh
:
IFS= read -rs 'password?Please enter a password: '
和bash
:
IFS= read -rsp 'Please enter a password: ' password
答案2
这是我的解决方案(已经过测试):
$ read -s PASSWORD
<user types password>
$ echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"'
解释:
read -s
读取一行标准输入(密码),而不将该行回显到屏幕上。它存储在 shell 变量 PASSWORD 中。echo -n $PASSWORD
将密码放在标准输出上,不带任何换行符。 (echo 是 shell 内置命令,因此不会创建新进程,因此(AFAIK)作为 echo 参数的密码不会显示在 ps 上。)- perl 被调用并从 stdin 读取密码
$_
- 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>
答案3
如果您的版本read
不支持,-s
请尝试 POSIX 兼容方式这个答案。
echo -n "USERNAME: "; read uname
echo -n "PASSWORD: "; set +a; stty -echo; read passwd; command <<<"$passwd"; set -a; stty echo; echo
passwd= # get rid of passwd possibly only necessary if running outside of a script
应该set +a
防止变量自动“导出”到环境中。您应该查看 的手册页stty
,有很多可用的选项。被<<<"$passwd"
引用是因为好的密码可以有空格。echo
启用后的最后一步stty echo
是让下一个命令/输出在新行上开始。