从未经授权的呼叫者处安全C#程序集

时间:2010-05-10 22:17:25

标签: c# security assemblies

有没有办法确保你的装配到班级/财产&类/方法级别,以防止从我们公司未签署的另一个程序集中使用/调用它们?

我想这样做,没有强烈命名的任何要求(比如使用StrongNameIdentityPermission)并坚持如何签署程序集。我真的不想使用InternalsVisibleTo属性,因为在不断变化的软件生态系统中无法维护。

例如:

情景一

Foo.dll由我公司签名,Bar.dll根本没有签名。

Foo有A级 酒吧有B级

A类有公共方法GetSomething() B类试图调用Foo.A.GetSomething()并被拒绝

拒绝可能是一个例外,或者在某种程度上被忽略

情景二

Foo.dll由我公司签名,Moo.dll也由我公司签名。

Foo有A级 Moo有C级

A类有公共方法GetSomething() C类试图调用Foo.A.GetSomething()并且不被拒绝

6 个答案:

答案 0 :(得分:6)

如果您希望将呼叫者限制为仅由特定证书进行了authenticode签名的代码,您仍然可以使用CAS(而不是StrongNameIdentityPermission)。

使用PublisherIdentityPermission就像使用任何CAS权限一样。或者,如果您想以声明方式进行,use an attribute

答案 1 :(得分:6)

显然,您必须对被调用方法中的每个调用执行检查 - 任何试图强制执行限制的外部系统都可以使用反射轻松绕过。

从方法中可以使用

new StackTrace().GetFrame(1).GetMethod().Module.Assembly

获取调用程序集。现在你可以使用

callingAssembly.GetName().GetPublicKey()

获取调用程序集的公钥,并将其与被调用程序集的公钥进行比较。如果它们匹配 - 假设所有程序集都使用相同的密钥对签名 - 则调用者被接受为合法调用者。

但是有一个循环漏洞 - 第三方程序集可以与您公司的公钥进行延迟签名,并从数字签名验证中排除。因此,加载程序将加载具有强名称的第三方程序集和公司公钥,即使它尚未签名。要关闭此循环孔,您必须检查签名。没有托管API,您必须进行P / Invoke

Boolean StrongNameSignatureVerificationEx(
   String wszFilePath,
   Boolean fForceVerification,
   ref Boolean  pfWasVerified)

fForceVerification设置为true并检查结果是否为true

所有这一切每次通话可能会产生很大的开销。诱惑可能是缓存结果,但假设一个具有反射权限的调用者,可能并不是很难操纵这样的缓存。另一方面,你永远不会100%肯定。谁控制系统可以自由地(几乎)做他想要的一切 - 附加调试器,修改内存内容,操作库或整个运行时。最后,您还必须有效地保护您的程序集免受反编译和修改。

答案 2 :(得分:2)

我见过公司编写的DLL(最着名的是Pegasus Imaging),它使用挑战/响应系统解锁装配。 DLL的购买者提供了与购买者名称相关联的“许可证代码”,然后DLL的消费者使用该代码来解锁DLL特征的特定子集。

因此,当应用程序第一次使用程序集时,会在程序集中调用Unlock()方法。用户名和解锁代码传入并运行验证身份的算法,可能使用某种公钥加密算法。

解锁代码中有一些编码用于指定功能;然后这些位在程序集中设置一些功能标志。所有调用函数都必须检查这些标志以确定是否启用了相应的功能。 Unlock()方法只调用一次,并且适用于已加载程序集的生命周期。

当然,既然你必须在程序集中提供“私钥”,这个程序不是防黑客的(什么是?),但它相当安全,并且会让诚实的人诚实。

答案 3 :(得分:2)

首先,正如您所知,使用InternalsVisibleTo是不够的 - 您还需要对每个程序集进行签名和强名,以确保某人不能仅仅欺骗名称。

现在已经开始了,你必须自己开发一个挑战 - 响应实现 - 除非你愿意使用明确的InternalsVisibleTo方法,否则你不能这样做描述你不想使用。

在C-R模型中,您需要在每次调用方法时传递某种令牌(或者只是为了实例化对象)。令牌将是一个只有你的代码才能创建实例的类 - 我会把它作为你想要使用的程序集的内部类,并使InternalsVisibleTo可以访问它 - 这样只需要一个类就可以了管理:

// SharedAssembly.dll
// marks ConsumingAssembly.dll as having access to internals...

internal sealed class AccessToken { }

public class SecuredClass
{
   public static bool WorkMethod( AccessToken token, string otherParameter )
   {
       if( token == null )
           throw new ArgumentException(); // you may want a custom exception.

       // do your business logic...
       return true;        
   }
}



// ConsumingAssembly.dll  (has access via InternalsVisibleTo)

public class MainClass
{
  public static void Main()
  {
      var token = new AccessToken(); // can create this because of IVT access
      SecuredClass.WorkMethod( token, "" );  // tada...
  }
}

您可能希望将AccessToken类放在服务提供者和使用者都知道的第三个程序集中,这样您就不必为不同的程序集不断维护不同的访问令牌类组。 / p>

为每种方法构建C-R机制既繁琐又乏味。它也不是100%万无一失 - 有足够时间和耐心的人可能会找到解决方法。

最佳选择(在您的情况下可能或可能不可能)将您的私有代码保留在您自己的服务器上,并仅将其作为Web服务(或类似的东西)公开。这使您可以主动管理IP的可访问性,并允许您以集中(而非分布)的方式更新访问者。已存在用于使用证书,消息签名和加密来限制对Web服务的访问的技术。这将是控制对您的IP访问的最可靠(并且经过验证)的方式。

答案 4 :(得分:2)

我觉得没什么大不了的!如果您真的需要安全性,请将您的代码放在服务器后面并使用客户端 - 服务器架构。或者是Web服务。或者像WCF或远程处理之类的东西。然后使用身份验证来验证客户端。

哎呀,你可以将所有内容设为私有,公开一个公共API并在本地根调用。

在仅限桌面的环境中保护未经授权的呼叫者的DLL只会使事情变得更复杂,更难以使用。更不用说内部看起来很丑陋了。

我看到一些会议正在出现。这可能对你有用。但它并没有为您提供所需的“全面安全性”。如果您有一个应该对客户隐藏的程序集,请不要将其放在GAC中。使用后缀为“INTERNAL”的名称空间。

答案 5 :(得分:0)

如果您不控制运行代码的执行环境,我认为没有办法实现此目的。在用户的计算机上以完全信任运行的代码将能够绕过您添加的任何限制。例如,完全信任代码可以使用反射API调用私有或内部方法,因此即使使用InternalsVisibleToAttribute也行不通。

如果您控制执行环境,您可以创建一个AppDomain,您的代码完全受信任,第三方代码部分受信任,除非您在AllowPartiallyTrustedCallersAttribute(APTCA)上添加SecurityCritical(APTCA),否则无法调用您的代码部件。您可以使用SecuritySafeCriticalHow to: Run Partially Trusted Code in a Sandbox属性限制在APTCA程序集中调用哪些方法。

{{3}}