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

显然,这是一场巨大的安全噩梦即将发生。

不幸的是,我们尚未在 .NET Framework 中发现任何类可以执行命令行程序,同时提供与 IDbCommand 对象为数据库所做的相同类型的注入攻击防护。

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

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

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

一些澄清:

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

解决方案

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

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

其他提示

当你 进程.开始 对于一个新进程,请在其参数参数中提供参数,而不是自己构建整个命令行。

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

明天将对此进行测试。

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

在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;
    }
}

但是,无论您使用什么方法,您都应该对其进行彻底的测试,看看它是否确实可以防止注入。

不要使用黑名单来防止注入。如果有 n 注入代码的方法,你会想到 纳米 在哪里 米 > 0.

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

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

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

嗯...

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

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

您还可以使用更多界面来解决问题,提供他们可以使用的命令和参数的下拉菜单。这对你来说是更多的工作,但它最终对用户有帮助。

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

@科特·哈根洛彻 反引号可以杀了你。如果 Windows 系统设置“错误”,或者 UNIX 系统允许,则 dir &bt;del *&bt;将首先执行 del * 命令,然后使用输出代替 del *,在这种情况下,这并不重要,因为没有任何内容可以 dir (或 ls)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top