假设 root 用户在环境变量中设置了一些凭据(例如,API 密钥),并且给定一个非特权用户 John,我怎样才能让 John 执行一个脚本,该脚本用途所述 API 密钥而不会有将其值泄露给 John 的风险?
答案1
问题好像有点不着重点。
问题是,本身, 谈到从“超级用户环境变量”(即“根环境变量”)获取值。我甚至不确定这是什么意思。您是说 John 应该从以“root”身份登录或以“root”身份通过或运行的(另一个)用户的环境中获取值吗
su
?sudo
或者您是在谈论过程 (可能是后台进程)以“root”身份运行?但是,无论你脑子里想的是什么细节,这听起来像是关键的动态和短暂。由于我不明白这意味着什么,我不会说这是不可能的,但这听起来很难。
但是随后在评论中您谈到使脚本可执行但不可读。这表明您愿意将密钥硬编码到脚本中,只要非 root 用户无法读取它。这表明密钥非常静态,很少更改,因此您愿意每次密钥更改时都修改脚本。
上层与下层关于这个话题有一个很长的帖子: 脚本是否可以执行但不可读? 遗憾的是,大多数答案都是否定的,或者是转移话题、转移注意力或变通方法,其中一些方法比其他方法更有效。例如,桑塔纳建议创建一个条目,
/etc/sudoers
允许 John跑步以提升的权限运行脚本,但无法以自己的身份读取它。但是,如果脚本不需要提升权限(除了获取密钥之外),您就不想以提升的权限运行它。然而,这个相关问题(超级用户),我介绍了一种笨拙的方法,即通过将其包装在 C 程序中来使脚本可执行但不可读。正如我在该答案中解释的那样,这种技术并非万无一失;信息可能会通过多种方式泄露。
在我看来,最合理/最可信的解释是密钥保存在 John 无法读取的文件中。实现此类安排的传统方式是 setUID 和/或 setGID。(
sudo
也很有用。)
下面是一个如何实现的示例。编写如下 C 程序:
#包括 <stdio.h> #包括 <stdlib.h> #定义密钥文件 (适当的路径名) #定义脚本 (适当的路径名) 主要的() { 文件*key_fp; 字符键[80]; (酌情调整) gid_t gid; uid_t uid; 字符 *args[10]; (酌情调整) key_fp = fopen(KEY_FILE,“r”); 如果 (key_fp == NULL) { 错误信息(KEY_FILE); 退出(1); } (您要阅读并验证的代码)钥匙) fclose(key_fp); 如果(setenv(“KEY”,键,1)!= 0) { fprintf(stderr, "setenv() 存在问题.\n"); 退出(1); } gid = 获取gid(); uid = 获取uid(); // 使用 // 如果 (setresgid(gid,gid,gid) != 0 || setresuid(uid,uid,uid) != 0) // 如果可用;否则 如果 (setregid (gid,gid) != 0 || setreuid (uid,uid) != 0) { fprintf(stderr, "删除权限时出现问题。\n"); 退出(1); } args[0] = "脚本名称"; (酌情调整) args[1] = 空; execv(脚本,参数); 错误(脚本); 退出(1); }
定义KEY_FILE
包含密钥的文件的完整路径名,并定义SCRIPT
包含脚本的文件的完整路径名。确保 John 具有脚本的读取权限,但没有密钥文件的读取权限,并且他没有对任何路径中的任何目录的写入权限。通过设置此程序的 setUID 和/或 setGID,使其能够读取密钥文件。它将读取密钥,将其插入环境,放弃权限,然后执行脚本。(您可能需要修改上述代码,以便将提供给程序的命令行参数传递给脚本。)
如上所述,脚本将使用 John 的 UID 和 GID 运行,但密钥在环境中。在某些系统上,John 可能能够从或 使用读取脚本的环境。如果是这样,让脚本立即将密钥复制到本地(非导出)变量并取消设置该变量可能会有所帮助。/proc/(pid_of_script)/environ
ps
KEY
脚本将密钥传递给程序可能是安全的
- 通过管道(例如),
printf "%s" (key_value) | (program)
- 通过此处的文档,或
- 通过此处的字符串。
上面提到了通过环境传递值的风险。避免将其作为命令行参数传递,因为这肯定可以被看到ps
。
如果您想使程序通过 运行sudo
,您可能需要对 John 的 UID 和 GID 进行硬编码,因为sudo
设置了真实 ID 和有效 ID。