这个围绕GPG的Ruby包装器有一个明显的安全漏洞吗?

时间:2014-04-08 18:46:14

标签: ruby security shell gnupg openpgp

代码

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注入 - 这是真的吗?
  • 恶意用户显然有可能利用这种方法进行密钥验证,如果有,您建议使用哪种解决方法?

1 个答案:

答案 0 :(得分:1)

嗯..它似乎非常简单而且不够健壮,但让我们这样看待它:

  1. GPG的命令行参数以单-结尾,因此IIRC GPG假设所有后续数据都作为数据,并且无法将其解释为其选项
  2. 它是精确GPG流程的stdin,而不是整个shell。如果注射成功,除了以奇怪的方式运行GPG之外几乎无法做任何其他事情。因此,即使攻击成功,如果GPG正​​确实现并且不会自行暴露任何攻击,那么只会失败GPG任务。 (我认为GPG的检查比Ruby的运行时和上面的脚本本身更好,所以看起来不太可能)
  3. commandexecve部分在我看来是胡说八道。 Command包含一些在代码中明显可见的字符串,以及一个以标准方式自动生成的temppath。一切都是常数或根本不依赖于用户,因此command与GPG的常量参数一样安全。
  4. 来自用户的唯一事情是传递给unsafe_input的{​​{1}}。它通过stdin传递给标准输入,以字符串形式传递给标准输入。 IIRC,它将在puts对象提供的to_s中结束,但它仍将最终将字符串数据写入管道,管道已被GPG视为数据。同样,它和GPG以及Ruby运行时一样安全。
  5. 所以,对我来说,这个剧本是“相对安全的” - “安全”,因为这是简单的代码而且我可以阅读它并且我没有看到任何问题 - 并且“相对”仅来自我不喜欢的事实认为自己是专家,无论是在Ruby中还是在安全方面。

    我可以说我相信GPG的代码。如果我正在寻找一些漏洞,我可能会在Ruby运行时自己寻找问题,缓冲区溢出等。我敢打赌,它比GPG检查的要少。即如果用户提供的数据具有不正确的字符编码和不正确的长度怎么办? unsafe_input puts会发疯吗?此外,我看到了许多其他或多或少的邪恶可能性:让我们即时注入定制的stream/pipe实施,并且安全性也随之而来。但除了这些之外,如果我们不允许“用户”加载他的任何代码,如果我使用Ruby来完成这项工作,并且如果我相信它的运行时 - 我发现这里没有问题。