我想表明通过输入密码read
是没有安全感的。
为了将其嵌入到一个半真实的场景中,假设我使用以下命令提示用户输入密码,并让 7z1 创建一个加密的存档:
read -s -p "Enter password: " pass && 7z a test_file.zip test_file -p"$pass"; unset pass
我第一次尝试泄露密码是通过设置一个审计规则:
auditctl -a always,exit -F path=/bin/7z -F perm=x
果然,当我执行涉及read
and的命令时7z
,运行时会出现一条日志条目ausearch -f /bin/7z
:
time->Thu Jan 23 18:37:06 2020
type=PROCTITLE msg=audit(1579801026.734:2688): proctitle=2F62696E2F7368002F7573722F62696E2F377A006100746573745F66696C652E7A697000746573745F66696C65002D7074686973206973207665727920736563726574
type=PATH msg=audit(1579801026.734:2688): item=2 name="/lib64/ld-linux-x86-64.so.2" inode=1969104 dev=08:03 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0
type=PATH msg=audit(1579801026.734:2688): item=1 name="/bin/sh" inode=1972625 dev=08:03 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0
type=PATH msg=audit(1579801026.734:2688): item=0 name="/usr/bin/7z" inode=1998961 dev=08:03 mode=0100755 ouid=0 ogid=0 rdev=00:00 nametype=NORMAL cap_fp=0 cap_fi=0 cap_fe=0 cap_fver=0 cap_frootid=0
type=CWD msg=audit(1579801026.734:2688): cwd="/home/mb/experiments"
type=EXECVE msg=audit(1579801026.734:2688): argc=6 a0="/bin/sh" a1="/usr/bin/7z" a2="a" a3="test_file.zip" a4="test_file" a5=2D7074686973206973207665727920736563726574
type=SYSCALL msg=audit(1579801026.734:2688): arch=c000003e syscall=59 success=yes exit=0 a0=563aa2479290 a1=563aa247d040 a2=563aa247fe10 a3=8 items=3 ppid=2690563 pid=2690868 auid=1000 uid=1000 gid=1000 euid=1000 suid=1000 fsuid=1000 egid=1000 sgid=1000 fsgid=1000 tty=pts17 ses=1 comm="7z" exe="/usr/bin/bash" key=(null)
这行似乎是最有希望的:
type=EXECVE msg=audit(1579801026.734:2688): argc=6 a0="/bin/sh" a1="/usr/bin/7z" a2="a" a3="test_file.zip" a4="test_file" a5=2D7074686973206973207665727920736563726574
但该字符串2D7074686973206973207665727920736563726574
不是我输入的密码。
我的问题有两个:
- 是
audit
获取密码的正确工具吗?如果是这样,我需要更改审核规则吗? - 除了 之外,还有更简单的方法
audit
来获取密码吗?
1我知道 7z 可以自行提示输入密码。
答案1
不安全的并不是read(2)
(从文件中读取数据的系统调用)。它甚至不是read(1)
(内置的 shell 从标准输入读取一行)。不安全的是在命令行上传递密码。
当用户输入 shell 用 读取的内容时read
,该内容对终端和 shell 都是可见的。它对其他用户不可见。使用 时read -s
,肩冲浪者将看不到它。
在命令行上传递的字符串在审核日志中可见。 (该字符串可能会被截断,我不确定这一点,但如果是的话,它只会比密码长得多的字符串。)当它包含空格等字符时,它只会以十六进制编码,这会使日志不明确来解析。
$ echo 2D7074686973206973207665727920736563726574 | xxd -r -p; echo
-pthis is very secret
$ perl -l -e 'print pack "H*", @ARGV' 2D7074686973206973207665727920736563726574
-pthis is very secret
这不是您不应该在命令行上传递秘密的主要原因。毕竟,只有管理员应该能够查看审核日志,并且管理员可以根据需要查看所有内容。然而,将秘密保存在日志中会更糟糕,因为以后更多的人可能会访问它们(例如通过不适当的安全备份)。
不应在命令行上传递机密的主要原因是,在大多数系统上,命令行对其他用户也是可见的。 (有些强化系统并非如此,但这通常不是默认设置。)任何在正确时间运行ps
、top
或cat /proc/*/cmdline
任何类似实用程序的人都可以看到密码。 7z 程序在启动后不久就会覆盖密码(一旦它能够制作内部副本),但这只能减少危险窗口,而不能消除漏洞。
在环境变量中传递秘密是安全的。该环境对其他用户不可见。但我认为 7z 不支持这一点。要传递密码而不使其通过命令行可见,您需要将其作为输入传递,并且 7z 从终端读取,而不是从 stdin 读取。您可以使用expect
这样做(或预期如果您更喜欢 Python 而不是 TCL,或者预计下午在 Perl 中,或者expect
在 Ruby 等中)。未经测试:
read -s -p "Enter password: " pass
pass=$pass expect \
-c 'spawn 7z a -p test_file.zip test_file' \
-c 'expect "assword:" {send $::env(pass)}' \
-c 'expect eof' -c 'catch wait result'
unset pass