Насколько уязвимым является выполнение ненадежного кода с ограничением ссылок с использованием AssemblyLoadContext в C# .NET Core
Я пытаюсь запустить ненадежные коды, загруженные пользователем на моем сервере. Мои пользователи хотят писать простые функции, которые будут выполняться на сервере, например:
public class HelloWorldPlugin
{
public string GetResult(string input)
{
//return System.IO.Directory.GetCurrentDirectory();
return "Hello " + input;
}
}
Обновление: мне не разрешено использовать докер или подобное изолированное решение из-за их требований к ресурсам.
Шаги следующие:
Определение разрешенных сборок и типов
Составление пользовательских кодов
Загрузка скомпилированной сборки в отдельный AssemblyLoadContext
Выполнение пользовательской функции
Распоряжение ресурсами
Я упростил свой код, как показано ниже:
public class PluginLoader : IDisposable
{
private AssemblyLoadContext assemblyLoadContext;
private Assembly assembly;
private byte[] bytes;
public void Load(string id, string code, IEnumerable<string> allowedAssemblyNames, IEnumerable<Type> allowedTypes)
{
var _references = new List<MetadataReference>();
foreach (var assemblyName in allowedAssemblyNames)
{
_references.Add(MetadataReference.CreateFromFile(RuntimeEnvironment.GetRuntimeDirectory() + assemblyName + ".dll"));
}
foreach (var type in allowedTypes)
{
_references.Add(MetadataReference.CreateFromFile(type.Assembly.Location));
}
var options = new CSharpCompilationOptions(
OutputKind.DynamicallyLinkedLibrary,
reportSuppressedDiagnostics: true,
optimizationLevel: OptimizationLevel.Release,
generalDiagnosticOption: ReportDiagnostic.Error,
allowUnsafe: false);
var syntaxTree = CSharpSyntaxTree.ParseText(code, options: new CSharpParseOptions(LanguageVersion.Latest, kind: SourceCodeKind.Regular));
var compilation = CSharpCompilation.Create(id, new[] { syntaxTree }, _references, options);
assemblyLoadContext = new AssemblyLoadContext(id, true);
using (var ms = new MemoryStream())
{
var result = compilation.Emit(ms);
if (result.Success)
{
ms.Seek(0, SeekOrigin.Begin);
bytes = ms.ToArray();
ms.Seek(0, SeekOrigin.Begin);
assembly = assemblyLoadContext.LoadFromStream(ms);
}
}
}
[MethodImpl(MethodImplOptions.NoInlining)]
public string Run(string typeName, string methodName, string input)
{
var instance = assembly.CreateInstance(typeName);
MethodInfo theMethod = instance.GetType().GetMethod(methodName);
return (string) theMethod.Invoke(instance, new[] { input });
}
public void Dispose()
{
assemblyLoadContext.Unload();
assemblyLoadContext = null;
bytes = null;
assembly = null;
}
}
И тестовый код:
var allowedAssemblies = new[] { "System", "System.Linq", "System.Linq.Expressions" };
var allowedTypes = new Type[] { typeof(object) };
string result;
using (var loader = new PluginLoader())
{
loader.Load("MyCustomPlugin", code, allowedAssemblies, allowedTypes);
result= loader.Run("HelloWorldPlugin", "GetResult", "World!");
}
Console.WriteLine(result);
Console.ReadLine();
Я знаю, что этот пользователь может иметь доступ к вводу-выводу, сети,... которые представляют собой огромные угрозы. Я попытался использовать System.IO.Directory.GetCurrentDirectory (): поскольку я не добавил сборку System.IO в allowedAssemblies, это вызывает исключение в разделе компиляции. Я также попытался получить текущую личность пользователя и несколько базовых работ с отражением, и все они вызывают исключение.
Если я ограничиваю разрешенные ссылки до компиляции, как пользователь может выполнить опасный код для доступа к ресурсам? Каковы угрозы, кроме выполнения непрерывного цикла (while True loop)?