代码:
require 'tempfile'
require 'open3'
def valid_pgp_key?(unsafe_user_input)
tempfile = Tempfile.new('pgp_pubkey')
command = ['gpg',
'--no-default-keyring',
'--keyring',
tempfile.path,
'--import',
'-']
stdin, stderrout, thread = Open3.popen2e(command)
stdin.puts(unsafe_user_input)
stdin.close
exit_status = thread.value
tempfile.unlink
exit_status.success? ? true : false
end
unsafe_user_input
是一个完全未经净化的字符串,我们希望它是一个有效的PGP公钥(但接受它可能是一个有效的密钥)。
问题:
- 我的理解是,将命令作为数组传递直接调用
execve()
,因此无需担心 shell 注入 - 这是真的吗? - 恶意用户是否显然有可能利用此方法进行密钥验证,如果是,您建议采取什么解决方法?
嗯。它似乎非常简单且不够健壮,但让我们这样看:
- GPG的命令行参数以单个
-
结尾,因此IIRC GPG假设以下所有数据都是数据,并且无法将其解释为其选项 - 它
stdin
精确的GPG过程,而不是整个外壳。如果注射成功,除了以奇怪的方式运行GPG之外,它几乎无能为力。因此,即使攻击成功,如果GPG正确实施并且没有自行暴露任何漏洞,它只会使GPG任务失败。(而且我假设 GPG 的检查比 Ruby 的运行时和上面的脚本本身更好,所以这似乎不太可能) - 在我看来,
command
和execve
部分是无稽之谈。该命令由代码中清晰可见的一些字符串和以标准方式自动生成的临时路径组成。一切都是恒定的或根本不依赖于用户,因此command
与 GPG 的常量参数一样安全。 - 来自用户的唯一内容是传递给
stdin
的unsafe_input
。它通过puts
传递,因此以字符串形式传递到标准输入。IIRC,它最终将以unsafe_input
对象提供的to_s
结束,但它仍然会以将字符串数据写入管道,而管道已经被 GPG 视为数据。同样,它与GPG和Ruby运行时一样安全。
因为那是简单的代码,我可以阅读它,我没有看到任何问题——"相对"只是因为我不认为自己是专家,无论是在 Ruby 还是在安全方面。
我可以说我相信GPG的代码。如果我正在寻找一些漏洞,我可能会在 Ruby 运行时中寻找问题、缓冲区溢出等。我敢打赌它比 GPG 检查得少。即,如果用户提供的数据具有格式不正确的字符编码和不正确的长度怎么办?puts
stream/pipe
会发疯吗?等。此外,我看到了许多其他或多或少邪恶的可能性:让我们即时注入定制的Open3.popen2e
实现,然后安全。但除此之外,如果我们不允许"用户"加载他的任何代码,如果我使用 Ruby 来完成这项工作,如果我信任它的运行时 - 我在这里看不到任何问题。