C# 使用Roslyn在服务器端API上安全执行代码

C# 使用Roslyn在服务器端API上安全执行代码,c#,security,roslyn,code-injection,roslyn-code-analysis,C#,Security,Roslyn,Code Injection,Roslyn Code Analysis,为了给我的用户更大的灵活性并让他们编写自己的表达式,我希望允许他们在文本字段中编写非常简单的C#语句,这些语句在服务器端执行,以进行一些自定义计算。 我正在与Roslyn一起存档。 我可以找到一个很好的例子 我允许用户在求值函数中插入代码,如下所示: string codeToCompile = @" using System; using System.Collections.Generic; namespace Eva

为了给我的用户更大的灵活性并让他们编写自己的表达式,我希望允许他们在文本字段中编写非常简单的C#语句,这些语句在服务器端执行,以进行一些自定义计算。 我正在与Roslyn一起存档。
我可以找到一个很好的例子

我允许用户在求值函数中插入代码,如下所示:

        string codeToCompile = @"
        using System;
        using System.Collections.Generic;
        namespace Evaluator
        {
            public class Evaluator
            {
                public string Eval()
                {
                    " + {POTENTIALLY_DANGEROUS_CODE_GOES_HERE} + @"                      
                }
            }
        }";
int a = 5;
int b = 10;

if(a < b) 
{
   return "a is smaller";
}
else
{
   return "a is bigger or equal";
}
您可以看到注入的代码总是在Eval函数中,并且应该在最后返回一个字符串。
用户可以决定如何计算此字符串。
我现在考虑的是安全性,因为我无法控制注入的代码

实际上,我的用户应该只能:

  • 使用数学表达式
  • 基本变量
  • 如果语句
因此,示例注入代码可能如下所示:

        string codeToCompile = @"
        using System;
        using System.Collections.Generic;
        namespace Evaluator
        {
            public class Evaluator
            {
                public string Eval()
                {
                    " + {POTENTIALLY_DANGEROUS_CODE_GOES_HERE} + @"                      
                }
            }
        }";
int a = 5;
int b = 10;

if(a < b) 
{
   return "a is smaller";
}
else
{
   return "a is bigger or equal";
}
inta=5;
int b=10;
if(a
您可以在上面的示例代码中看到,名称空间仅限于“System”和“System.Collections.Generic”,因此许多内容将不再可能(例如从服务器的文件系统读取某些内容并将这些信息作为字符串输出)

我还替换所有出现的循环,因此表达式如while、for、foreach等。。。从字符串中删除

但我仍然很不确定这个解决方案是否安全

  • 潜在的攻击者现在还能做什么?(特别是使用提供的两个名称空间选项)
  • 在这种情况下,我能做些什么来防止攻击,有什么最佳实践吗

  • 根据您的需要,这很难做到正确。很难。“如果你要问怎么做,你可能会被问得头晕目眩”。考虑一些有趣的事情:

  • 仅仅因为您限制了文件顶部的名称空间,并不意味着某人不能显式地将代码段中的某些内容限定为不同的名称空间。因此,重要的是您必须遍历整个代码,以查看是否有其他类型的使用。我无法判断您所允许的显式列表是否隐式禁止所有方法调用或对象创建
  • 要小心假设系统中的任何东西都是安全的。考虑Soal.Ac激活程序,它允许调用CeaLeVal事例并传递另一个类型的字符串名称来创建它。仅此类型就可以绕过可能执行的任何其他检查。当我按字母顺序调出系统名称空间中的文档时,这只是第一个跳出来的
  • …当然,不要只是阻塞系统。特别是激活器。每当您更新人们编写代码所针对的框架时,可能会有新的类型出现问题
  • 也考虑你潜在的安全攻击类型:即使你不能写文件,你仍然可以从服务器中泄露信息(比如用户名或机器名),这可能允许用户以其他方式入侵你的系统。或者他们只是编写一个无限循环,它会消耗服务器资源。您提到要删除循环,但不要忘记goto之类的事情,或者只是编写某种递归函数来实现堆栈溢出

    我不会说“只要做X就安全了”,因为我甚至不相信自己会写这个。但是:

  • 使用你的操作系统来帮助你隔离:在一个单独的进程中运行它,权限更少或没有权限,等等。如果你可以单独使用VM/容器,那就太好了。在这里隔离得越多越好
  • 如果您要进行代码检查,不要拒绝您知道不好的模式;相反,编写只接受您知道是“安全”的模式的代码。这可能会导致大量的工作来选择愚蠢的事情,但另一种选择要求你列举所有不好的事情

  • 根据您的需要,这很难做到正确。很难。“如果你要问怎么做,你可能会被问得头晕目眩”。考虑一些有趣的事情:

  • 仅仅因为您限制了文件顶部的名称空间,并不意味着某人不能显式地将代码段中的某些内容限定为不同的名称空间。因此,重要的是您必须遍历整个代码,以查看是否有其他类型的使用。我无法判断您所允许的显式列表是否隐式禁止所有方法调用或对象创建
  • 要小心假设系统中的任何东西都是安全的。考虑Soal.Ac激活程序,它允许调用CeaLeVal事例并传递另一个类型的字符串名称来创建它。仅此类型就可以绕过可能执行的任何其他检查。当我按字母顺序调出系统名称空间中的文档时,这只是第一个跳出来的
  • …当然,不要只是阻塞系统。特别是激活器。每当您更新人们编写代码所针对的框架时,可能会有新的类型出现问题
  • 也考虑你潜在的安全攻击类型:即使你不能写文件,你仍然可以从服务器中泄露信息(比如用户名或机器名),这可能允许用户以其他方式入侵你的系统。或者他们只是编写一个无限循环,它会消耗服务器资源。您提到要删除循环,但不要忘记goto之类的事情,或者只是编写某种递归函数来实现堆栈溢出

    我不会说“只要做X就安全了”,因为我甚至不相信自己会写这个。但是:

  • 使用你的操作系统来帮助你隔离:在一个单独的进程中运行它,权限更少或没有权限,等等。如果你可以单独使用VM/容器,那就太好了。在这里隔离得越多越好
  • 如果您要进行代码检查,不要拒绝您知道不好的模式;相反,编写只接受您知道是“安全”的模式的代码。这可能会导致大量的工作来选择愚蠢的事情,但另一种选择需要