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

有没有办法将您的程序集保护到类/属性和类/方法级别,以防止从我们公司未签名的另一个程序集中使用/调用它们?

我想这样做没有强烈命名的任何要求(比如使用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()并且不被拒绝

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

使用PublisherIdentityPermission就像使用任何CAS权限一样。 或者,如果要以声明方式执行此操作,请使用属性 。

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

从您可以使用的方法中

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

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

 callingAssembly.GetName().GetPublicKey() 

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

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

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

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

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

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

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

在CR模型中,您需要在每次方法调用时传递某种令牌(或者只是为了实例化一个对象)。 令牌将是一个只有你的代码才能创建实例的类 – 我会将它作为你想要使用的程序集的内部类,并使其可以通过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类放在服务提供者和使用者都知道的第三个程序集中,这样您就不必为不同的程序集不断维护不同的访问令牌类组。

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

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

我见过由公司(最着名的Pegasus Imaging)编写的DLL,它使用挑战/响应系统解锁组件。 DLL的购买者提供有“购买者名称”的“许可证代码”,DLL的消费者随后使用该许可证代码来解锁DLL特征的特定子集。

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

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

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

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

哎呀,你可以把一切都私有,公开一个公共API并在本地根调用。

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

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

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

如果您控制执行环境,则可以创建一个AppDomain,其中您的代码完全受信任,第三方代码部分受信任,除非您在程序集上放置AllowPartiallyTrustedCallersAttribute (APTCA),否则无法调用您的代码。 您可以使用SecurityCritical和SecuritySafeCritical属性限制可以在APTCA程序集中调用哪些方法。

如何:在沙盒中运行部分受信任的代码