[英]Is there a mechanism to disable Reflection in .NET 6?
我正在編寫一個可擴展的服務器應用程序。 我想公開一個第三方可以使用的庫來編寫服務器應用程序的擴展。 當我加載第 3 方程序集時,如果程序集使用反射,我想阻止加載它。
目標是允許第 3 方庫開發人員實現服務器接口,服務器將執行這些實現,但如果使用反射,則會阻止加載程序集。
這背后的目的是在將第 3 方程序集加載到服務器時保持合理的安全級別。 IE 第 3 方程序集應該受到限制,不能使用反射來訪問應用程序的其他部分。
我閱讀了有關裝配證據的信息,但我不知道這是否能讓我收集到我需要知道的信息。
當我加載第 3 方程序集時,如果程序集使用反射,我想阻止加載它
你有一個沒有好的解決方案的難題。 沒有很好的方法來驗證某些圖書館將做什么。 您可以實施一些啟發式方法(如下),但可能有足夠積極性的人可以找到繞過這些檢查的方法。 以下內容適用於 do.net core,但可以在 do.net 框架上實現,只需稍作改動。
此代碼執行兩個分析:
“顯而易見”的方法是調用assembly.GetReferencedAssemblies()
並檢查System.Reflection
是否存在,但實際上這失敗了,因為反射是由System.Runtime
提供的,並且沒有單獨的反射程序集。 但是您至少可以使用該過程來檢查其他程序集。
因此,似乎需要更仔細地檢查源代碼。 Mono項目調試器庫, Mono.Cecil
用到了。 有關相關示例,請參閱此問題。
ILSpy可能有助於確定檢查的方式/內容。
我創建了兩個用於測試的虛擬項目。 這些是 .net 標准庫,編譯為 dll 然后放入主運行時文件夾中。 這些將在運行時檢查,而不提供對程序集的硬引用。
項目 1——可能會做一些您不希望它做的事情,但至少它沒有使用反射!
using System.Net.Sockets;
namespace LibrarySafe
{
public class ModuleLibrarySafe
{
public bool DoSomething(string args)
{
new TcpClient(args.Split(',')[0], int.Parse(args.Split(',')[1])).GetStream().Write(System.Text.Encoding.ASCII.GetBytes("yeah"), 0, 5);
return true;
}
}
}
項目 2——使用反射:
using System.Reflection;
namespace LibraryUnsafe
{
public class ModuleLibraryUnsafe
{
public bool DoSomething(string args)
{
return Assembly.GetExecutingAssembly().DefinedTypes.Any(x => x.FullName == args);
}
}
}
以下是一種用於確定是否使用反射的緩解策略(不是解決方案)。 Console.WriteLine
部分指示與禁止命名空間的匹配。 我在需要擴展/適應您的用例的領域留下了一些評論。 以下是不完整的,不包括您需要考慮的一些 IL(我留下了我所知道的缺失的評論;也許還有更多我不知道的)。
最后,這可以使用一些異常處理,並且您可能需要運行時緩存或某種哈希集來跟蹤之前看到的方法調用。
祝你好運。
使用 nuget package System.Reflection.MetadataLoadContext
using Mono.Cecil;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Reflection.Metadata;
using System.Runtime.InteropServices;
namespace FindReferencedAssemblies
{
internal class Program
{
// List of namespaces that will be checked against.
private static List<string> _unsafeAssemblyNames = new List<string>()
{
"System.Reflection",
};
static void Main(string[] args)
{
string prefix = Directory.GetCurrentDirectory();
// need to pick up required/running dotnet libraries or PathAssemblyResolver will fail.
var requiredAssemblyFilesnames = new List<string>(Directory.GetFiles(RuntimeEnvironment.GetRuntimeDirectory(), "*.dll"));
// now add assemblies to check. This would come from your runtime module to load but is hard coded here.
var toCheckAssemblyFilesnames = new List<string>()
{
Path.Combine(prefix, "LibrarySafe.dll"),
Path.Combine(prefix, "LibraryUnsafe.dll"),
};
var resolver = new PathAssemblyResolver(requiredAssemblyFilesnames.Concat(toCheckAssemblyFilesnames));
// dotnetcore: assembly metadata is only available withing this "using" context
using (var metadataContext = new MetadataLoadContext(resolver))
{
foreach (var filename in toCheckAssemblyFilesnames)
{
// dotnet framework: use Assembly.ReflectionOnlyLoad
var assembly = metadataContext.LoadFromAssemblyPath(filename);
var assemblyShortName = assembly.GetName().Name;
var assemblyFullName = assembly.FullName;
// Check all referenced assemblies against the list of banned namespaces.
var referencedAssemblies = assembly.GetReferencedAssemblies().ToList();
foreach (var assemblyReference in referencedAssemblies)
{
var unsafeAsm = _unsafeAssemblyNames.FirstOrDefault(x => string.Compare(x, assemblyReference.Name) == 0);
if (!string.IsNullOrEmpty(unsafeAsm))
{
Console.WriteLine($"Found unsafe reference to [{unsafeAsm}] in assembly [{assemblyFullName}]");
}
}
// Now switch over to looking at method calls within the assembly.
var cecilAssemblyDefinition = Mono.Cecil.AssemblyDefinition.ReadAssembly(filename);
// Get a list of all types defined within the assembly.
var assemblyTypes = cecilAssemblyDefinition.MainModule.GetTypes()
// here, `StartsWith` may or may not be sufficient
.Where(x => x.FullName.StartsWith(assemblyShortName));
foreach (var type in assemblyTypes)
{
// Get a list of all methods defined on the type.
foreach (var method in type.Methods)
{
// Find references to methods called, from within the method we are considering.
var calledMethods = method.Body.Instructions
.Where(x =>
x.OpCode == Mono.Cecil.Cil.OpCodes.Call
&& x.Operand is MethodReference)
.Select(x => x.Operand)
.Cast<MethodReference>()
.ToList();
// TODO: perform the same check against `Operand is MethodDefinition`
// TODO: perform the same two checks against `x.OpCode == Mono.Cecil.Cil.OpCodes.Callvirt`
// Iterate the list of methods called, and compare against the list of banned namespaces.
foreach (var methodRef in calledMethods)
{
// here, `StartsWith` may or may not be sufficient
var unsafeAsmMatch = _unsafeAssemblyNames.Where(x => methodRef.FullName.StartsWith(x));
foreach (var match in unsafeAsmMatch)
{
Console.WriteLine($"Found unsafe reference to [{match}] in assembly [{assemblyFullName}], method [{method.FullName}]");
}
}
}
}
}
}
}
}
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.