在 Ruby 中形成卫生 shell 命令或系统调用
我正在构建一个守护进程来帮助我管理我的服务器。 Webmin 工作得很好,就像打开服务器的 shell 一样,但我更希望能够从我设计的 UI 控制服务器操作,并向最终用户公开一些功能。
守护进程将从队列中选取操作并执行它们。但是,由于我将接受用户的输入,因此我想确保他们不允许将危险的东西注入特权 shell 命令中。
这是一个说明我的问题的片段:
def perform
system "usermod -p #{@options['shadow']} #{@options['username']}"
end
解释更多的要点: https://gist.github.com/773292
我如果典型的转义和清理输入足以满足这种情况,那么情况并不乐观,并且作为一名设计师,我没有大量与安全相关的经验。 我知道这对我来说可能是显而易见的,但事实并非如此!
如何确保将创建和序列化操作的 Web 应用程序无法将危险文本传递到接收操作的特权进程中?
感谢您的帮助
套利
I'm building a daemon that will help me manage my server(s). Webmin works fine, as does just opening a shell to the server, but I'd prefer to be able to control server operations from a UI I design, and also expose some functionality to end users.
The daemon will pick up actions from a queue and execute them. However, since I'll be accepting input from users, I want to make sure they're not permitted to inject something dangerous into a privileged shell command.
Here's a fragment that exemplifies my problem:
def perform
system "usermod -p #{@options['shadow']} #{@options['username']}"
end
A gist that explains more: https://gist.github.com/773292
I'm not positive if typical escaping and sanitizing of inputs is enough for this case, and being a designer, I don't have a ton of security-related experience. I know that this is something that should probably be obvious to me, but its not!
How can I ensure that the web application that will create and serialize the actions can't pass dangerous text into the privileged process that receives the actions?
Thanks for the help
arb
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
看起来您不需要外壳来完成您正在做的事情。请参阅此处的
system
文档:http://ruby -doc.org/core/classes/Kernel.html#M001441您应该使用
system
的第二种形式。上面的示例将变成:更好的(IMO)编写方式是:
这种方式的参数直接传递到 execve 调用中,因此您不必担心偷偷摸摸的 shell 技巧。
It doesn't look like you need a shell for what you're doing. See the documentation for
system
here: http://ruby-doc.org/core/classes/Kernel.html#M001441You should use the second form of
system
. Your example above would become:A nicer (IMO) way to write this is:
The arguments this way are passed directly into the
execve
call, so you don't have to worry about sneaky shell tricks.如果您不仅需要退出状态,还需要结果,您可能想使用 Open3.popen3:
更多信息请参见:获取 Ruby 中 system() 调用的输出
If you need not just the exit status but also the result you probably want to use
Open3.popen3
:More information here: Getting output of system() calls in Ruby
我建议研究“shellwords”模块。该脚本:
输出转义文本和预期输出:
I'd suggest looking into the 'shellwords' module. This script:
outputs the escaped text and the expected output:
这是一个老问题,但由于它几乎是您在谷歌搜索时找到的唯一真正答案,我想我应该添加一个警告。系统的多参数版本在 Linux 上似乎相当安全,但在 Windows 上却不是。
尝试
系统“dir”、“&”、“echo”、“hi!”
在 Windows 系统上。 dir 和 echo 都将运行。当然,Echo 也可能是一种远没有那么无害的东西。
This is an old question, but since it's pretty much the only real answer you'll find when googling I thought I'd add a caveat. The multi argument version of system seems reasonably safe on Linux, but it is NOT on Windows.
Try
system "dir", "&", "echo", "hi!"
on a Windows system. Both dir and echo will be run. Echo could of course just as well be something far less innocuous.
我知道这是一个旧线程,但还有另一个选项被 Simon Hürlimann 轻轻提及。
关于这个主题的信息并不多,我认为这可能会帮助其他有需要的人。
在此示例中,我们将使用
Open3
,它使您能够同步或异步运行命令,并提供 stdout、stderr、退出代码和PID。Open3 允许您访问 stdout、stderr、退出代码以及在运行另一个程序时等待子进程的线程。您可以使用与 Process.spawn 相同的方式指定程序的各种属性、重定向、当前目录等。 (来源:Open3 文档 )
我选择将输出格式化为
CommandStatus
对象。其中包含我们的stdout
、stderr
、pid
(工作线程的)和exitstatus
。在读取 STDOUT/ERR 缓冲区时,如果 stdout_buffer.length > 则使用 command_stdout = stdout_buffer 0 来控制是否分配
command_stdout
变量。当不存在数据时,您应该传递nil
而不是""
。后面交数据的时候就更清楚了。您可能注意到我使用了
command + ';'
。其原因基于 Kernel.exec 的文档(这是 popen3 使用的):这只是防止 Ruby 在您传递格式错误的命令时抛出
'spawn': No such file or directory
错误。相反,它会将其直接传递到内核,错误将在内核中得到妥善解决并显示为 STDERR 而不是未捕获的异常。I know this is an old thread, but there is another option that was lightly touched on by Simon Hürlimann.
There is not a lot of information about this topic and I think this might help others in need.
For this example we'll use
Open3
which gives you the ability to run commands synchronously or asynchronously, and provides stdout, stderr, exit codes, and PID.Open3 grants you access to stdout, stderr, exit codes and a thread to wait for the child process when running another program. You can specify various attributes, redirections, current directory, etc., of the program in the same way as for Process.spawn. (Source: Open3 Docs)
I chose to format the output as a
CommandStatus
object. This contains ourstdout
,stderr
,pid
(Of the worker thread) andexitstatus
.While reading the STDOUT/ERR buffers, I use
command_stdout = stdout_buffer if stdout_buffer.length > 0
to control whether thecommand_stdout
variable is assigned or not. You should passnil
instead of""
when no data is present. It's more clear when handing data later on.You probably noticed me using
command + ';'
. The reason for this is based on the documentation from Kernel.exec (Which is what popen3 uses):This simply prevents a Ruby from throwing a
'spawn': No such file or directory
error if you pass a malformed command. Instead it will pass it straight to the kernel where the error will be resolved gracefully and appear as STDERR instead of an uncaught exception.现代、安全且简单的解决方案(
popen
将为您转义参数):(#read
将在返回之前关闭 IO)Modern, secure and simple solution (
popen
will escape arguments for you):(
#read
will close the IO before returning)