You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 5/.NET 6中如何创建沙箱执行C#字符串代码?

在.NET 5/6中创建安全沙箱执行C#字符串代码

要在.NET 5/6中安全执行动态C#字符串代码,同时隔离外部环境、防止恶意代码注入,可采用以下几种方案,核心思路是编译隔离+权限限制:

方案一:基于AssemblyLoadContext的进程内隔离沙箱

利用Roslyn编译动态代码为内存程序集,通过自定义AssemblyLoadContext隔离加载,并配合权限集限制代码操作范围,避免影响外部环境。

步骤1:编译动态代码为隔离程序集

首先引用Microsoft.CodeAnalysis.CSharp NuGet包,编译用户代码时仅允许引用基础核心程序集,禁止危险依赖:

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using System.Reflection;
using System.Runtime.Loader;
using System.Security;
using System.Security.Permissions;

public static Assembly CompileSafeCode(string userCode)
{
    // 解析用户代码语法树
    var syntaxTree = CSharpSyntaxTree.ParseText(userCode);
    
    // 仅允许引用核心基础程序集,避免危险依赖
    var allowedReferences = new[]
    {
        MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
        MetadataReference.CreateFromFile(typeof(int).Assembly.Location)
    };

    // 编译为动态链接库
    var compilation = CSharpCompilation.Create(
        "SandboxedAssembly",
        new[] { syntaxTree },
        allowedReferences,
        new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));

    using var ms = new MemoryStream();
    var emitResult = compilation.Emit(ms);
    
    if (!emitResult.Success)
    {
        var errors = string.Join("\n", emitResult.Diagnostics
            .Where(d => d.Severity == DiagnosticSeverity.Error)
            .Select(d => d.GetMessage()));
        throw new InvalidOperationException($"代码编译失败:{errors}");
    }

    ms.Seek(0, SeekOrigin.Begin);
    // 用自定义AssemblyLoadContext加载程序集,实现隔离
    var sandboxAlc = new SandboxAssemblyLoadContext();
    return sandboxAlc.LoadFromStream(ms);
}

// 自定义AssemblyLoadContext,限制外部程序集加载
public class SandboxAssemblyLoadContext : AssemblyLoadContext
{
    public SandboxAssemblyLoadContext() : base(isCollectible: true) { }

    protected override Assembly Load(AssemblyName assemblyName)
    {
        // 仅允许加载核心系统程序集,禁止加载自定义或危险程序集
        var allowedAssemblies = new[] { "System.Private.CoreLib", "System.Runtime" };
        if (allowedAssemblies.Contains(assemblyName.Name))
        {
            return LoadFromAssemblyName(assemblyName);
        }
        throw new SecurityException("禁止加载未授权的外部程序集");
    }
}

步骤2:配置权限集并执行代码

通过PermissionSet限制沙箱内代码的操作权限(仅保留执行权限,禁止文件IO、网络、反射等危险操作),然后反射调用目标方法:

public static object ExecuteSandboxedCode(string userCode, string methodName, params object[] parameters)
{
    // 应用权限限制:仅允许执行代码,无其他权限
    var permissionSet = new PermissionSet(PermissionState.None);
    permissionSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
    permissionSet.Demand();

    var assembly = CompileSafeCode(userCode);
    var targetType = assembly.GetTypes().First();
    var instance = Activator.CreateInstance(targetType);
    var method = targetType.GetMethod(methodName);

    if (method == null)
    {
        throw new InvalidOperationException($"未找到方法:{methodName}");
    }

    return method.Invoke(instance, parameters);
}

// 调用示例
var userCode = @"
public class Calculator
{
    public int WhichBigger(int param1, int param2)
    {
        return param1 > param2 ? param1 : param2;
    }
}";

var result = (int)ExecuteSandboxedCode(userCode, "WhichBigger", 10, 20);
// result = 20

额外安全强化

  • 静态代码扫描:编译前对用户代码进行语法分析,检测File.Delete、Process.Start、Assembly.Load等危险关键字,提前拦截恶意代码。
  • 超时限制:为代码执行添加超时控制,防止死循环或长时间占用资源。

方案二:进程外容器隔离(更高安全级别)

如果需要更彻底的隔离,可将代码执行逻辑放在Docker容器中:

  • 将动态代码编译、执行的逻辑封装为独立服务,部署在Docker容器内。
  • 主程序通过HTTP或IPC调用容器内的服务传递代码和参数,获取执行结果。
  • 容器配置严格的资源限制(CPU、内存)和网络隔离,即使代码恶意执行也无法影响主机环境。

这种方案安全性最高,但实现复杂度和性能开销也更大,适合高风险场景。


内容的提问来源于stack exchange,提问作者sergio cascales AV

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 11:01:04