2008-09-04 163 views
9

我们目前正在构建一个执行大量外部工具的应用程序。我们经常需要将由用户输入到我们系统中的信息传递给这些工具。防止命令行注入攻击

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

不幸的是,我们还没有发现.NET框架中的任何类可以执行命令行程序,同时提供与IDbCommand对象针对数据库相同的注入攻击防范。

现在,我们使用一个非常原始的字符串替换我怀疑是相当不足:

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

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

一些澄清:

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

回答

5

您是直接执行程序还是执行shell?如果您始终通过给可执行文件提供完整路径名称并将shell从等式中移出来启动外部程序,那么您不会受到任何类型的命令行注入的影响。

编辑:DrFloyd,壳是负责评估像反色的东西。没有外壳,没有外壳评估。显然,你仍然需要知道你所调用的程序中存在任何潜在的安全问题 - 但我不认为这个问题就是这个问题。

0

那么,如果你可以在没有命令行的情况下以编程方式调用工具,那可能是你最好的选择。否则,你可以通过一个完全不能访问的用户来执行命令行工具(除了可能不会有任何伤害的单个目录之外)...尽管这可能最终会破坏工具,具体取决于该工具的功能。

请注意,我从来没有面对过这个问题,因为我从来没有真的必须从外部应用程序中调用一个命令行工具,其中该工具需要用户输入。

0

嗯...

这听起来像你有有效的命令列表的用户能够执行。但是你不希望他们全部执行。

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

你也可以用更多的接口来解决问题,提供一个可以使用的命令和参数的下拉菜单。这是更多的工作,但最终会帮助用户。

0

您是直接执行程序还是执行shell?如果您始终通过给可执行文件提供完整路径名称并将shell从等式中移出来启动外部程序,那么您不会受到任何类型的命令行注入的影响。

@Curt Hagenlocher反向会杀死你。如果Windows系统设置为“错误”,或者unix系统允许,那么将会首先执行del *命令,然后使用输出代替del *,在这种情况下,这并不重要因为没有任何目录(或ls)

2

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

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

明天会测试一下。

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

2

在C++上的Windows,你只是逃避\和“在需要的地方,引用参数和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; 
    } 
} 

但是,随着你使用任何方法,你应该测试地狱离开它,看,它确确实实预防注射。

1

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

使用可接受参数(或模式)的白名单。自然界限制更多,但这就是安全的本质。