我们目前正在构建一个执行许多外部工具的应用程序。我们经常要将用户输入我们系统的信息传递给这些工具。
显然,这是一场等待发生的大安全噩梦。
不幸的是,我们还没有在.NET Framework中找到执行命令行程序的任何类,同时提供与IDbCommand对象对数据库相同的防止注入攻击的防范。
现在,我们正在使用非常原始的字符串替换,我怀疑它是不够的:
protected virtual string Escape(string value) { return value .Replace(@"\", @"\\") .Replace(@"$", @"\$") .Replace(@"""", @"\""") .Replace("`", "'") ; }
你们做了什么来防止命令行注入攻击?我们计划实现一个非常严格的正则表达式,只允许一小部分字符通过,但我想知道是否有更好的方法。
一些澄清:
答案 0 :(得分:5)
您是直接执行程序还是通过shell执行?如果你总是通过给可执行文件提供完整的路径名来启动一个外部程序,并使shell不在等式中,那么你就不会受到任何命令行注入的影响。
编辑:DrFloyd,shell负责评估反引号等内容。没有shell,没有shell评估。显然,你仍然需要知道你正在调用的程序中存在任何潜在的安全问题 - 但我不认为这个问题与此有关。答案 1 :(得分:2)
当您Process.Start新进程时,请在其Parameters参数中提供参数,而不是自己构建整个命令行。
没有时间进行适当的测试,但我认为这应该有助于保护它。
明天会测试一下。
编辑:啊,有人再打我一次。但这是另一点:尝试使用Console.InputStream(不记得确切的名称)来提供数据而不是传递参数,这是一个可能的解决方案吗?比如修复命令,使其从CON设备读取,然后通过输入流提供数据。
答案 2 :(得分: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;
}
}
但是,使用你使用的任何方法,你应该测试它的地狱,看它确实可以防止注射。
答案 3 :(得分:1)
请勿使用黑名单来防止注射。如果有 n 方法注入代码,你会想到 n - m 其中 m&gt; 0
使用已接受参数(或模式)的白名单。它本质上更具限制性,但这就是安全性。
答案 4 :(得分:0)
好吧,如果你可以在没有命令行的情况下以编程方式调用这些工具,那么这可能是你最好的选择。否则,你可能会通过一个完全没有访问权限的用户来执行命令行工具(除了可能没有任何损害的单个目录)......尽管这可能最终会破坏工具,具体取决于该工具的功能。
请注意,我从来没有遇到过这个问题,因为我从来没有必要从面向外部的应用程序中调用命令行工具,而该工具需要用户输入。
答案 5 :(得分:0)
嗯...
听起来你有一个用户能够执行的有效命令列表。但是你不希望他们全部执行它们。
您可以尝试使用实际的命令行,并至少验证文件是否存在于“安全”位置。
您还可以通过更多界面解决问题,提供可以使用的命令和参数下拉。这是你的最终工作,但它最终会帮助用户。
答案 6 :(得分:0)
您是直接执行程序还是通过shell执行?如果你总是通过给可执行文件提供完整的路径名来启动一个外部程序,并使shell不在等式中,那么你就不会受到任何命令行注入的影响。
@Curt Hagenlocher 反击可以杀死你。如果Windows系统设置为“错误”,或者unix系统允许,则为dir&amp; bt; del *&amp; bt;将首先执行del *命令然后使用输出代替del *,在这种情况下,这将无关紧要,因为dir(或ls)没有任何内容