25

我有一个引用托管 DLL 的 .NET 应用程序。

这个 DLL 包含一个类,比如ScoreKeeper实现一个名为GetHighScore(). 应用程序会定期调用它。

有没有办法阻止 .NET 应用程序在这里使用“未经授权”的 DLL 来代替我提供的那个?

4

2 回答 2

51

你提到:

这个 DLL 包含一个类,比如ScoreKeeper实现了一个名为 GetHighScore() 的方法。应用程序会定期调用它。

接着:

有没有办法阻止 .NET 应用程序在这里使用“未经授权”的 DLL 来代替我提供的那个?

假设您想阻止某人将您提供的程序集换成他们自己的程序集,该程序集具有相同的名称和类型(位于同一命名空间中),您可以 将强名称应用于包含ScoreKeeper该类的程序集并让您的消费者参考。

但是,我们会看到一些问题使得这不是 100% 可靠的。强名称可帮助您保护不知情的用户免受恶意欺骗副本替换您的 DLL。但是,如果用户是欺骗的同谋(如果他试图作弊,就会出现这种情况),那么代码签名将只不过是一个减速带,并不能提供真正的保护。当然,强名称不提供与例如 PunkBuster 相媲美的保护。

使用强名称验证程序集发布者的身份

当您向程序集添加强名称时,您正在使用私钥(非对称公钥/私钥对的一部分,稍后将详细介绍)生成加密哈希,并且公钥包含在程序集名称中(以及哈希)。

使用公共散列和公钥,CLR 能够验证程序集的签名确实来自私钥。

当然,这意味着,您应该保护密钥(内部和外部);如果其他人拥有您的密钥,那么他们可以有效地冒充您并发布人们相信来自您的程序集。

然后,当您添加对已签名程序集的引用时,如果有人尝试使用相同的程序集名称(不是完全限定的程序集,只是没有版本、哈希和公钥的名称)和相同类型名称的不同程序集,则尝试加载类型时CLR填充失败,表示找不到;使用完全限定的程序集名称以及命名空间和类型名称来解析类型。

为什么强名称不是 100% 安全的(是什么?)

1)哈希冲突

仍然是一个正在验证的哈希。虽然散列非常大(默认散列算法 SHA-1 为 160 位),但任何具有有限数量值的散列都会发生冲突。虽然不可能,但它可能的(不可能与不可能)。此外,默认情况下仅使用最后 8 个字节。结合研究表明 SHA-1 相对较弱,这是使用MSDN中所述的 SHA-256 增强型强命名的一个很好的理由。

2)删除强名称

可以删除强名称。但是,在这种情况下,由于您的程序集引用了被引用程序集的强命名版本,因此当您的程序集尝试使用受损版本时,它将在运行时失败,假设您已正确重新启用验证(见下文)。

3)对程序集的物理访问是指所有程序集

如果有人可以访问物理机器并且可以修改您引用的程序集,那么您的程序集同样容易受到攻击。如果攻击者能够修改您引用的程序集的强名称,那么他们就可以轻松地修改您的程序集和所有其他参与执行的程序集。为此,100% 确保物理程序集未被黑客入侵的唯一方法是拒绝通过它进行物理访问。当然,这带来了一系列不同的安全问题。

4)管理员禁用强名称检查

计算机管理员可以简单地绕过强名称检查,使用sn -Vr. 根据MSDN

注册程序集以跳过验证...恶意程序集可以使用添加到跳过验证列表的程序集的完全指定的程序集名称(程序集名称、版本、文化和公钥令牌)来伪造其身份。这将允许恶意程序集也跳过验证。

5)必须在 .NET 3.5 SP 1 之后显式启用强名称检查

从 .NET 3.5 SP 1 开始,仅仅拥有一个强名称并不能提供任何保护

从 .NET Framework 3.5 Service Pack 1 (SP1) 开始,当程序集加载到完全信任的 AppDomain 对象(例如 MyComputer 区域的默认 AppDomain)中时,不会验证强名称签名。

为了让 .NET 检查加载到应用程序中的每个程序集的强名称,您需要将以下代码段(由 MSDN 提供)插入到应用程序配置文件中:

<configuration>
  <runtime>
     <bypassTrustedAppStrongNames enabled="false" />
  </runtime>
</configuration>

但是请注意,这只能防止强名称被删除。

当您覆盖绕过功能时,仅验证强名称的正确性;它没有检查 a StrongNameIdentityPermission。如果要确认特定的强名称,则必须单独执行该检查。


如果考虑到上述问题,您仍然想为您的程序集进行强命名,这里是如何。

生成强名称并签署您的程序集

在生成强名称时,您有两种选择使用哪个键。在 Visual Studio 中,转到项目属性上的“签名”选项卡,然后单击“签署程序集”:

从那里,您有两个选项来生成公钥/私钥,让 VS.NET 为您生成密钥,或者指向现有的密钥:

选择“新建”时,Visual Studio 将提示您输入要生成的文件的名称,以及是否要选择使用密码来访问它:

创建强名称键对话框

此时,密钥将添加到您的项目中:

添加到项目的密钥

现在,您可以将其移至解决方案项(如果您的解决方案中有多个项目)。

在这种情况下,Visual Studio 实际上只是调用强名称命令行工具来生成公钥和私钥对。如果您想自己执行此操作,则需要sn.exe使用-k命令行选项调用以生成密钥,如下所示:

sn -k keyPair.snk

然后通过上面的“浏览”对话框添加它。

请注意,当您执行此操作时,它会将密钥拉入您的项目。如果您不想这样做(因为它会将密钥复制到每个项目中),请从项目中删除密钥,然后将现有文件添加到项目中,但链接。这将清除“选择强名称密钥文件”选项,但如果将其下拉,您将看到链接密钥文件的完整路径。

于 2012-11-12T15:17:55.007 回答
3

似乎最直接的答案是计算您正在加载的 DLL 的安全哈希,然后将其与您预先计算的黄金值进行比较。当然,这仍然很容易被足够坚定的攻击者破解,但它会显着提高想要作弊的人的门槛。

于 2012-11-12T15:17:23.767 回答