#c# #security #networking #io #registry
#c# #Безопасность #сеть #io #реестр
Вопрос:
Предполагая, что мое приложение реализует систему плагинов / расширений, которая позволяет пользователю динамически загружать и выполнять код из внешних DLL-файлов, как я могу управлять безопасностью с точки зрения доступа к диску, реестру и сети (чтение / запись / удаление)?
Чтобы быть более конкретным, я хотел бы иметь возможность знать, когда плагин / расширение собирается выполнить операцию с диском / реестром / сетью, перехватить это действие и разрешить или запретить его на основе набора предопределенных правил или просто спросив пользователя, разрешают ли они определенный тип доступа к этому конкретному плагину.
Комментарии:
1. Предполагается, что это проверка работоспособности или граница безопасности? Для последнего вашими единственными вариантами являются (1) отдельный процесс, запущенный от имени другого пользователя, или (2) интерпретируемый (или JIT, скомпилированный компилятором, которым вы управляете) язык сценариев, который просто не имеет возможности напрямую обращаться к системным вызовам. Просто нет способа запретить двоичному коду в процессе делать все, что он хочет (до предела того, что вы можете сделать).
2. Примечательно, что код пользовательского режима всегда может избежать попыток другого кода пользовательского режима перехватить его. Всего несколько способов: (a) передать допустимые аргументы в утвержденный интерфейс, но из другого потока перезаписать имя файла непосредственно перед его передачей в ОС. (b) Обойти утвержденный интерфейс и динамически подключаться к функциям ОС напрямую. (c) Сделайте это вообще без вызова какой-либо функции API библиотеки, вместо этого загрузите параметры и выполните системный вызов.
3. @BenVoigt грубо говоря, способ, которым работает реализация моего плагина / расширений, заключается в том, что он загружает точку входа из допустимых библиотек DLL расширений, и эти библиотеки DLL расширений могут затем делать все, что угодно, через хост-приложение. Я ищу систему мониторинга, которая способна обнаруживать операции с диском / реестром / сетью и блокировать или разрешать такие операции на основе набора правил или пользовательского ввода (аналогично тому, как работает антивирусное программное обеспечение).
4. @BenVoigt Наличие второго процесса, отслеживающего процесс, который загружает расширения, звучит как хорошая идея, и я бы согласился с этим, и в связи с этим я бы предпочел примеры кода c #, которые демонстрируют, как один процесс может обнаруживать активность диска / реестра / сети другого и приостанавливать его до тех пор, пока пользователь не разрешит или не запретит эту операцию (я предполагаю, что именно так работают AVS). Так что да, я бы пошел по этому пути.
5. @IneedHelp: для этого вам понадобится код в режиме ядра. Ваша проблема в том, что вы пытаетесь установить контроль доступа после выбора вашей реализации, когда ее действительно нужно разработать до написания одной строки кода.
Ответ №1:
Для абсолютного решения вам придется использовать драйвер ядра для перехвата запросов. но если вам не нужно 100%, вы можете разработать инструмент, аналогичный API Monitor.
Инструмент подключает функции API (таких библиотек немного, MSDetour, Mhook и т.д.) И может записывать вызов или прерывать вызов. узнать, какой модуль вызвал API, также возможно, изучив стек.
Ответ №2:
Некоторый контроль может быть достигнут с помощью доменов приложений и безопасности доступа к коду. Вы можете создать изолированный AppDomain и предоставить ему разрешения для каталогов, к которым вы хотите, чтобы ваш плагин имел доступ. Если плагин пытается получить доступ к файлу вне их, вы получаете SecurityException.
Например, если у нас есть следующее ClassLib.dll плагин:
namespace ClassLib
{
public class Class1
{
public static string DoWork(string path)
{
return System.IO.File.ReadAllText(path);
}
}
}
Мы можем вызывать ее с ограниченными разрешениями, подобными этому:
using System;
using System.Collections;
using System.Reflection;
using System.Security;
using System.Security.Policy;
using System.Security.Permissions;
namespace ConsoleApplication1
{
class Program
{
//Path to plugin library
public static string libname = @"C:PROJECTSConsoleApp1binDebugClassLib.dll";
//Invokes plugin library in restricted AppDomain
//Grants access to the directory specified by allowedPath parameter
static void InvokeLibrary(string allowedPath="")
{
var path = new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath;
//create restricted permission set
Evidence evidence = new Evidence();
evidence.AddHostEvidence(new Zone(SecurityZone.Internet));
PermissionSet permissionSet = SecurityManager.GetStandardSandbox(evidence);
//grant read access to plugin library file, so it can at least load successfully
permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, libname));
permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.PathDiscovery, libname));
//grant read access to
if (allowedPath != "")
{
permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read, allowedPath));
}
AppDomainSetup appDomainSetup = new AppDomainSetup();
appDomainSetup.ApplicationBase = System.IO.Path.GetDirectoryName(path);
//create AppDomain with specified permissions
AppDomain domain = AppDomain.CreateDomain(
"MyDomain",
evidence,
appDomainSetup,
permissionSet
);
//create remoting wrapper
Type type = typeof(Wrapper);
Wrapper wrapper = (Wrapper)domain.CreateInstanceAndUnwrap(
type.Assembly.FullName,
type.FullName);
//invoke method
InvokeResult res = wrapper.Invoke(libname,"c:\dir\file.txt");
if (res.Success) Console.WriteLine("Invoke result: " res.Result);
else {
Console.WriteLine("Invoke error: " res.Error.GetType().ToString());
if (res.Error is SecurityException)
{
Console.WriteLine("Demanded permissions:n" (res.Error as SecurityException).Demanded);
}
}
AppDomain.Unload(domain);
}
static void Main(string[] args)
{
//invoke plugin without explicit permissions
Console.WriteLine("First attempt...");
InvokeLibrary();
//invoke plugin with specified directory access
Console.WriteLine("Second attempt...");
InvokeLibrary("c:\dir\");
Console.ReadKey();
}
}
//Object to pass between app domains
public class InvokeResult : MarshalByRefObject
{
public bool Success = false;
public string Result="";
public Exception Error =null;
}
//Wrapper for remoting
public class Wrapper : MarshalByRefObject
{
public Exception lasterror;
public InvokeResult Invoke(string lib, string arg)
{
InvokeResult res = new InvokeResult();
try
{
//load plugin
Assembly ass = Assembly.LoadFile(lib);
Type t = ass.GetType("ClassLib.Class1");
MethodInfo mi = t.GetMethod("DoWork", BindingFlags.Static | BindingFlags.Public);
//invoke plugin method
res.Result = (string)mi.Invoke(null, new object[] { arg });
res.Success = true;
}
catch (TargetInvocationException ex)
{
res.Success = false;
if (ex.InnerException != null) res.Error = ex.InnerException;
else res.Error = ex;
}
return res;
}
}
}
/* Output:
First attempt...
Invoke error: System.Security.SecurityException
Demanded permissions:
<IPermission class="System.Security.Permissions.FileIOPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
version="1"
Read="c:dirfile.txt"/>
Second attempt...
Invoke result: The content of file.txt
*/