10

我们目前正在构建一个执行许多外部工具的应用程序。我们经常不得不将用户输入我们系统的信息传递给这些工具。

显然,这是一场等待发生的重大安全噩梦。

不幸的是,我们还没有在 .NET Framework 中找到任何执行命令行程序的类,同时提供与 IDbCommand 对象对数据库所做的相同类型的防范注入攻击。

现在,我们正在使用一个非常原始的字符串替换,我怀疑这还不够:

protected virtual string Escape(string value)
{
      return value
        .Replace(@"\", @"\\")
        .Replace(@"$", @"\$")
        .Replace(@"""", @"\""")
        .Replace("`", "'")
      ;
}

你们如何防止命令行注入攻击?我们计划实现一个非常严格的正则表达式,只允许一小部分字符通过,但我想知道是否有更好的方法。

一些澄清:

  • 其中一些工具没有我们可以编程的 API。如果他们这样做了,我们就不会遇到这个问题。
  • 用户不会选择要执行的工具,他们会输入我们选择的工具使用的元数据(例如,将版权声明等元数据注入目标文件)。
4

7 回答 7

5

你是直接执行程序还是通过shell?如果您总是通过提供可执行文件的完整路径名并将 shell 排除在等式之外来启动外部程序,那么您实际上不会受到任何类型的命令行注入的影响。

编辑: DrFloyd,外壳负责评估反引号之类的东西。无壳,无壳评价。显然,您仍然必须了解您所调用的程序中的任何潜在安全问题——但我认为这个问题与此无关。

于 2008-09-04T21:18:10.723 回答
2

当您Process.Start一个新进程时,请在其 Parameters 参数中提供参数,而不是自己构建整个命令行。

没有时间进行适当的测试,但我认为这应该有助于在一定程度上保护它。

明天将对此进行测试。

编辑:啊,有人再次击败我。但这里还有一点:尝试使用 Console.InputStream (不记得确切的名称)来提供数据而不是传递参数,这是一个可能的解决方案吗?比如修复命令,使其从 CON 设备读取,然后您通过输入流提供数据。

于 2008-09-04T21:30:07.107 回答
2

Windows上的 C++ 中,您只需转义 \ 和 " 在需要的地方,引用参数并 ShellExecute 它。然后,引号内的所有内容都应视为文本。

这应该说明:


#include <iostream>
#include <string>
#include <windows.h>
#include <cstdlib>
using namespace std;

// Escape and quote string for use as Windows command line argument
string qEscape(const string& s) {
    string result("\"");
    for (string::const_iterator i = s.begin(); i != s.end(); ++i) {
        const char c = *i;
        const string::const_iterator next = i + 1;
        if (c == '"' || (c == '\\' && (next == s.end() || *next == '"'))) {
            result += '\\';
        }
        result += c;
    }
    result += '"';
    return result;
}

int main() {
    // Argument value to pass: c:\program files\test\test.exe
    const string safe_program = qEscape("c:\\program files\\test\\test.exe");
    cout << safe_program << " ";

    // Argument value to pass: You're the "best" around.
    const string safe_arg0 = qEscape("You're the \"best\" around.");

    // Argument value to pass: "Nothing's" gonna ever keep you down.
    const string safe_arg1 = qEscape("\"Nothing's\" gonna ever keep you down.");

    const string safe_args = safe_arg0 + " " + safe_arg1;
    cout << safe_args << "\n\n";

    // c:\program files\test\  to pass.
    const string bs_at_end_example = qEscape("c:\\program files\\test\\");
    cout << bs_at_end_example << "\n\n";

    const int result = reinterpret_cast<int>(ShellExecute(NULL, "open", safe_program.c_str(), safe_args.c_str(), NULL, SW_SHOWNORMAL));
    if (result < 33) {
        cout << "ShellExecute failed with Error code " << result << "\n";
        return EXIT_FAILURE;
    }
}

但是,对于您使用的任何方法,您都应该对其进行测试,看看它确实可以防止注入。

于 2008-09-07T22:05:48.583 回答
2

不要使用黑名单来防止注射。如果有n种注入代码的方法,你会想到n - m where m > 0

使用可接受参数(或模式)的白名单。它本质上更具限制性,但这就是安全的本质。

于 2008-10-23T13:51:08.220 回答
0

好吧,如果您可以在没有命令行的情况下以编程方式调用这些工具,那可能是您的最佳选择。否则,您可能会通过绝对无权执行任何操作的用户执行命令行工具(除了可能无法对其造成任何伤害的单个目录)......尽管最终可能会破坏该工具,具体取决于关于工具的作用。

请注意,我从来没有遇到过这个问题,因为我实际上从来没有从需要用户输入的面向外部的应用程序中调用命令行工具。

于 2008-09-04T21:19:52.963 回答
0

嗯……

听起来您有一个用户可以执行的有效命令列表。但是您不希望他们全部执行。

您可以尝试使用实际的命令行并至少验证文件是否存在于“安全”位置。

您还可以通过更多界面解决问题,提供他们可以使用的命令和参数的下拉列表。你需要做更多的工作,但它最终会帮助用户。

于 2008-09-04T21:21:49.683 回答
0

你是直接执行程序还是通过shell?如果您总是通过提供可执行文件的完整路径名并将 shell 排除在等式之外来启动外部程序,那么您实际上不会受到任何类型的命令行注入的影响。

@Curt Hagenlocher反引号可以杀死你。如果 Windows 系统设置“错误”,或者 unix 系统允许,则目录 &bt;del *&bt; 将首先执行 del * 命令,然后使用输出代替 del *,在这种情况下,这无关紧要,因为 dir(或 ls)没有任何内容

于 2008-09-04T21:27:33.790 回答