保护 C# 程序集免受未经授权的调用者的攻击

发布于 2024-09-01 00:32:08 字数 584 浏览 5 评论 0原文

有什么方法可以确保您的程序集符合类别/属性和属性?类/方法级别以防止从未经我们公司签名的另一个程序集使用/调用它们?

我希望在不需要强命名(例如使用 StrongNameIdentityPermission)的情况下执行此操作,并坚持程序集的签名方式。我真的不想诉诸使用 InternalsVisibleTo 属性,因为这在不断变化的软件生态系统中是不可维护的。

例如:

场景一

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

Foo 具有 A 级 Bar 具有 B 类

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

被拒绝可能是异常或以某种方式被忽略

场景二

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

Foo 具有 A 级 Moo 有 C 类

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

Is there any way to secure your assembly down to the class/property & class/method level to prevent the using/calling of them from another assembly that isn't signed by our company?

I would like to do this without any requirements on strong naming (like using StrongNameIdentityPermission) and stick with how an assembly is signed. I really do not want to resort to using the InternalsVisibleTo attribute as that is not maintainable in a ever changing software ecosystem.

For example:

Scenario One

Foo.dll is signed by my company and Bar.dll is not signed at all.

Foo has Class A
Bar has Class B

Class A has public method GetSomething()
Class B tries to call Foo.A.GetSomething() and is rejected

Rejected can be an exception or being ignored in someway

Scenario Two

Foo.dll is signed by my company and Moo.dll is also signed by my company.

Foo has Class A
Moo has Class C

Class A has public method GetSomething()
Class C tries to call Foo.A.GetSomething() and is not rejected

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

只想待在家 2024-09-08 00:32:08

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

使用 PublisherIdentityPermission 就像使用任何CAS 权限。或者,如果您想以声明方式执行此操作,使用属性< /a>.

If you are wanting to limit the callers to only code that has been authenticode signed by a specific certificate, you can still use CAS (just not StrongNameIdentityPermission).

Use PublisherIdentityPermission just like you would have used any CAS permissions. Or if you want to do it declaratively, use an attribute.

这个俗人 2024-09-08 00:32:08

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

在该方法中,您可以使用它

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

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

callingAssembly.GetName().GetPublicKey()

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

但存在一个漏洞 - 可以使用您公司的公钥延迟对第 3 方程序集进行签名,并将其排除在数字签名验证之外。因此,加载程序将加载具有强名称和您公司公钥的第 3 方程序集,即使它尚未签名。要堵住这个漏洞,你必须检查签名。没有托管 API,您必须将

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

fForceVerification 设置为 true 进行 P/Invoke,并检查结果是否为 true

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

Obviously you have to perform a check on every call from within the called method - any external system trying to enforce the restrictions is easily bypassed using reflection.

From within the method you can use

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

to get the calling assembly. Now you can use

callingAssembly.GetName().GetPublicKey()

to obtain the public key of the calling assembly and compare it with the public key of the called assembly. If they match - assuming all your assemblies are signed with the same key pair - the caller is accepted as a legitimated caller.

But there is one loop hole - a 3rd party assembly can be delay signed with your companies public key and excluded from the digital signature verification. In consequence the loader will load the 3rd party assembly with a strong name and your companies public key even if it is not yet signed. To close this loop hole you have to check the signature. There is no managed API and you have to P/Invoke

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

with fForceVerification set to true and check if the result is true.

All together this may be quite a lot overhead per call. The temptation is probably to cache the result but assuming a caller with reflection permission it is probably not very hard to manipulate such a cache. On the other hand you will never be 100% sure. Who ever controls the system is free to do (almost) everything he wants - attach an debugger, modify memory content, manipulate libraries or the whole runtime. Finally you have to efficiently protect your assembly from decompilation and modification, too.

调妓 2024-09-08 00:32:08

首先,正如您所意识到的,仅使用 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% 万无一失——有足够时间和耐心的人可能会找到解决方法。

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

First of all, as you realize, it's not enough to use InternalsVisibleTo - you would also need to sign and strongly-name each assembly to ensure someone can't just spoof the name.

Now that that's out of the way, you would have to develop a challenge-response implementation on your own - this isn't something you can do unless you're willing to use the InternalsVisibleTo approach that you explicitly describe you don't want to use.

In a C-R model, you would need to pass some kind of token with every method call (or perhaps just to instantiate an object). The token would be a class that only your code can create an instance of - I would make this an internal class of the assembly you want to consume and make it accessible with InternalsVisibleTo - this way only a single class needs to be managed:

// 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...
  }
}

You may want to put the AccessToken class in a third assembly that both the service provider and consumer know about so that you don't have to constantly maintain a different group for access token classes for different assemblies.

Building a C-R mechanism for every method is cumbersome and tedious. It also isn't 100% foolproof - someone with enough time and patience could probably find a way around it.

The best option (which may or may not be possible in your case) would be to keep your private code on your own servers and only expose it as a webservice (or something similar). This allows you to actively manage accessiblity to your IP and allows you to update who has access in a centralized (rather than distributed) manner. Technologies already exist to restrict access to web services using certificates, message-signature, and encryption. This would be the most reliable (and proven) way to control access to you IP.

﹏雨一样淡蓝的深情 2024-09-08 00:32:08

我见过一些公司(最著名的是 Pegasus Imaging)编写的 DLL,它们使用质询/响应系统来解锁程序集。 DLL 的购买者会获得一个与购买者姓名相关的“许可证代码”,DLL 的使用者随后使用该代码来解锁 DLL 功能的特定子集。

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

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

当然,由于您必须在程序集中提供“私钥”,因此此过程并不是防黑客的(什么是?),但它相当安全,并且会让诚实的人保持诚实。

I have seen DLL's written by companies (most notably Pegasus Imaging) that use a challenge/response system to unlock the assembly. The purchaser of the DLL is provided with a "License Code," tied to the name of the purchaser, which the consumer of the DLL then uses to unlock a specific subset of the DLL's features.

So when the assembly is used the first time by the application, the Unlock() method is called in the assembly. The user name and unlock code are passed in and run through an algorithm that verifies identity, presumably using a Public Key Encryption algorithm of some sort.

There are some bits encoded in the unlock code that specify features; these bits then set some feature flags in the assembly. All calling functions must check these flags to determine if the appropriate feature is enabled. The Unlock() method is called only once, and is good for the lifetime of the loaded assembly.

Of course, since you have to provide the "private key" in the assembly, this procedure is not hack-proof (what is?), but it is reasonably secure, and will keep the honest people honest.

时光病人 2024-09-08 00:32:08

我觉得这也太小题大做了吧!如果您确实想要安全性,请将代码放在服务器后面并使用客户端-服务器架构。或者网络服务。或者介于两者之间的东西,例如 WCF 或远程处理。然后使用身份验证来验证客户端的身份。

哎呀,您可以将所有内容设为私有,公开公共 API 并在本地进行调用。

在纯桌面环境中保护 dll 免受未经授权的调用者的攻击只会使事情变得更加复杂且难以处理。更不用说它的内部看起来很丑陋。

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

I think it's too much fuss for nothing! If you really want security, put your code behind a server and use a client-server architecture. Or web services. Or something in between like WCF, or remoting. Then use authentication to authenticate a client.

Heck you can make everything private, expose a public API and root the calls locally.

Securing a dll from unauthorized callers in a desktop only environment only makes matters more complicated and harder to work with. Not to mention that it would look pretty ugly on the inside.

I see some conventions emerging. And that may work for you. But it doesn't give you the "total security" you require. If you have an assembly that is supposed to be hidden from customers, don't put it in the GAC. Use namespaces postfixed with something like "INTERNAL".

素手挽清风 2024-09-08 00:32:08

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

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

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

I don't think there's a way to do this if you don't control the execution environment under which the code is run. Code running with full trust on a user's machine would be able to get around any restrictions you added. For example, full trust code could invoke private or internal methods with the reflection APIs, so even using the InternalsVisibleToAttribute wouldn't work.

If you control the execution environment, you can create an AppDomain where your code is fully trusted, and third party code is partially trusted and can't call your code unless you put an AllowPartiallyTrustedCallersAttribute (APTCA) on the assembly. You can restrict which methods can be called in an APTCA assembly with the SecurityCritical and SecuritySafeCritical attributes.

How to: Run Partially Trusted Code in a Sandbox

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文