コマンドラインインジェクション攻撃の防止
-
09-06-2019 - |
質問
現在、多数の外部ツールを実行するアプリケーションを構築しています。多くの場合、ユーザーがシステムに入力した情報をこれらのツールに渡す必要があります。
明らかに、これはセキュリティ上の大きな悪夢が待っています。
残念ながら、.NET Framework には、IDbCommand オブジェクトがデータベースに対して行うのと同じ種類のインジェクション攻撃に対する防御を提供しながら、コマンド ライン プログラムを実行するクラスはまだ見つかりません。
現在、非常に原始的な文字列置換を使用していますが、これでは不十分だと思われます。
protected virtual string Escape(string value) { return value .Replace(@"\", @"\\") .Replace(@"$", @"\$") .Replace(@"""", @"\""") .Replace("`", "'") ; }
コマンドラインインジェクション攻撃を防ぐために皆さんは何をしていますか?私たちは非常に厳密で、ごく少数の文字のサブセットのみを許可する正規表現を実装する予定ですが、もっと良い方法はないかと考えていました。
いくつかの説明:
- これらのツールの中には、プログラミングできる API を持たないものもあります。もしそうなら、私たちはこの問題に悩まされることはなかったでしょう。
- ユーザーは実行するツールを選択するのではなく、選択したツールが使用するメタデータを入力します (たとえば、著作権表示などのメタデータをターゲット ファイルに挿入します)。
解決
プログラムを直接実行していますか、それともシェルを介して実行していますか?常に実行可能ファイルに絶対パス名を指定し、シェルを考慮に入れないことで外部プログラムを起動する場合は、あらゆる種類のコマンド ライン インジェクションの影響を実際に受けることはありません。
編集:DrFloyd、シェルはバッククォートなどを評価する責任があります。シェルもシェル評価もありません。もちろん、呼び出しているプログラムに潜在的なセキュリティの問題があることを認識しておく必要がありますが、この質問はそれに関するものではないと思います。
他のヒント
あなたが プロセス.開始 新しいプロセスでは、コマンド ライン全体を自分で構築する代わりに、その Parameters 引数にパラメータを指定します。
適切なテストを行う時間がありませんが、ある程度のレベルまでは保護できるはずです。
明日これをテストします。
編集: ああ、また誰かにやられた。しかし、ここで別の点があります。パラメーターを渡す代わりに 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 コードを挿入する方法は次のとおりです。 n - m どこ m > 0.
受け入れられるパラメータ (またはパターン) のホワイトリストを使用します。本質的にははるかに制限的ですが、それがセキュリティの性質です。
コマンド ラインを使用せずにプログラムでツールを呼び出すことができるのであれば、おそらくそれが最善の選択肢でしょう。そうしないと、何もするためのアクセス権がまったくないユーザー (おそらく、害を及ぼすことができない単一のディレクトリを除く) を介してコマンド ライン ツールを実行する可能性があります。ただし、ツールの動作によっては、ツールが壊れる可能性があります。
ツールがユーザーからの入力を必要とする外部アプリケーションからコマンド ライン ツールを実際に呼び出す必要がなかったので、私はこの問題に直面したことがないことに注意してください。
うーん...
ユーザーが実行できる有効なコマンドのリストがあるようです。しかし、それらをすべて実行してほしくないのです。
実際のコマンドラインを実行して、ファイルが少なくとも「安全な」場所に存在することを確認してください。
また、より多くのインターフェイスを使用して問題を解決し、使用できるコマンドとパラメータのドロップダウンを提供することもできます。あなたの側では手間がかかりますが、最終的にはユーザーの助けになります。
プログラムを直接実行していますか、それともシェルを介して実行していますか?常に実行可能ファイルに絶対パス名を指定し、シェルを考慮に入れないことで外部プログラムを起動する場合は、あらゆる種類のコマンド ライン インジェクションの影響を実際に受けることはありません。
@カート・ハーゲンロッチャー バックティックは命を落とす可能性があります。Windows システムのセットアップが「間違っている」場合、または UNIX システムでそれが許可されている場合、 dir &bt;del *&bt;最初に del * コマンドを実行し、次に del * の代わりに出力を使用します。この場合、 dir (または ls) には何もないため、これは問題になりません。