强名称可以用来验证程序集的作者吗?

22

我一直在阅读MSDN中的适当文章, 强名称程序集以及一个相关的Stack Overflow问题,检查程序集是否具有强名称.

  1. 强名称程序集可以在多大程度上被验证以避免篡改?
  2. 是否可能使用强名称验证程序集作者?

第一个问题是在阅读CSharp411文章.NET Assembly FAQ - Part 3 - Strong Names and Signing之后引起的,其中提到了这个问题,以及使用强名称的其他问题:

"不能防止完全替换。 强名称无法阻止黑客删除强名称签名,恶意修改您的程序集,使用自己的密钥重新签名,然后把他的程序集假冒成您的程序集。"

第二个问题旨在找出强命名和其他签名方案(如Authenticode)之间的区别。同一篇早期的MSDN文章指出:

"请注意,强名称本身并不意味着像数字签名和支持证书所提供的那样的信任级别。"

我试图使用强名称做更多的事情,超出了它的创建目的吗?强命名只是为了避免名称冲突或全局程序集缓存(DLL Hell)的新形式吗?

5个回答

23
当您使用基于私钥创建的强名称对程序集进行签名时,会有以下好处:
  • 强名称通过向程序集添加公钥令牌和数字签名来保证程序集标识的唯一性。
  • 可以将强名称与公钥匹配以证明该程序集来自具有该公钥的发布者且仅来自该发布者。
  • 强名称提供了强大的完整性检查。通过.NET Framework安全检查可以保证程序集内容自上次构建以来未被更改。

是否可以使用强命名验证程序集作者?

是的,如上所述,强命名可以验证程序集的最新作者。但它不能验证原始作者。如果攻击者替换了您的程序集的强名称,那么只能验证您不是程序集的最新作者。如果他删除了强名称,则根本无法进行任何作者验证。

强名称程序集可以在多大程度上被验证以避免篡改?

下面的C#代码验证攻击者是否篡改了应用强名称时写入程序集的公钥令牌。它不能避免篡改,但可以检测某些类型的篡改。以下方法接受一个包含您的公钥令牌的字节数组,并将其与程序集的实际令牌进行比较。请注意,为使此技术有效,您选择的混淆器应加密包含您的公钥令牌的字符串,并且仅在使用时动态解密它。还要注意,您需要具有FullTrust权限才能使此代码工作,因为它在幕后使用反射。
// Check that public key token matches what's expected.
private static bool IsPublicTokenOkay_Check(byte [] tokenExpected)
{
    // Retrieve token from current assembly
    byte [] tokenCurrent = Assembly.GetExecutingAssembly().GetName().GetPublicKeyToken();

    // Check that lengths match
    if (tokenExpected.Length == tokenCurrent.Length)
    {
        // Check that token contents match
        for (int i = 0; i < tokenCurrent.Length; i++)
            if (tokenExpected[i] != tokenCurrent[i]) 
                return false;
    }
    else
    {
        return false;
    }
    return true;
}

只要你运行的是.NET Framework 3.5 SP1之前的版本,你也可以强制验证强名称签名,以防止强名称被攻击者删除或在注册表中禁用强名称检查。以下代码演示了调用另一个名为NativeMethods的静态方法的过程,在此处将执行验证。
// Check that this assembly has a strong name.
private bool IsStrongNameValid_Check()
{
    byte wasVerified = Convert.ToByte(false); 
     byte forceVerification = Convert.ToByte(true);
    string assemblyName = AppDomain.CurrentDomain.BaseDirectory + 
                          AppDomain.CurrentDomain.FriendlyName; 
    return NativeMethods.CheckSignature(assemblyName, 
                                        forceVerification, 
                                        ref wasVerified);
}

实际的签名验证使用 P/Invoke 进行,如下所示。使用 StrongNameSignatureVerificationEx API 相当复杂 - 详细解释请参见这篇博客文章
// P/Invoke to check various security settings
// Using byte for arguments rather than bool, 
// because bool won't work on 64-bit Windows!
[DllImport("mscoree.dll", CharSet=CharSet.Unicode)]
private static extern bool StrongNameSignatureVerificationEx(string wszFilePath, 
                                                             byte fForceVerification, 
                                                             ref byte pfWasVerified);

// Private constructor because this type has no non-static members
private NativeMethods()
{
}

public static bool CheckSignature(string assemblyName, 
                                  byte forceVerification, 
                                  ref byte wasVerified)
{
    return StrongNameSignatureVerificationEx(assemblyName, 
                                             forceVerification, 
                                             ref wasVerified );
}

注意,默认情况下,这不适用于使用.NET 3.5 SP1或更高版本的应用程序,该版本具有强名称绕过功能。可以通过向配置文件添加设置来禁用此功能,使其适用于您的应用程序。但是,任何拥有读/写访问权限的攻击者都可以撤销您的决定。

谢谢!很棒的例子。这个检查会忽略/绕过哪些类型的篡改? - Pablo Marambio
1
这段代码无法检测攻击者更改您的公钥令牌副本以匹配其自己的公钥的情况。当然,它也无法检测到代码本身被阻止运行的篡改。 - HTTP 410
@RoadWarrior:太棒了。我得再考虑一下这个。 - Hamish Smith
这需要 .NET 3.5。对于已安装 .NET 4 及更高版本的 Windows 8 计算机,如何调整此代码以验证强名称签名? - Marcus
@Igor,我已经更新了我的答案来回答你的问题。 - HTTP 410
预期的公钥令牌检查 + StrongNameSignatureVerificationEx与“wasVerified”必须为true。即使使用强命名绕过,这也适用。MSDN声明:如果由于注册表设置而成功验证,则pfWasVerified也将设置为false。 - Martin.Martinsson

12

Authenticode 依赖于第三方证书颁发机构进行证书验证。Strong Naming 的工作原理类似于自签名证书,并可以像自签名证书一样处理。它确实使用标准数字签名,但问题在于验证程序集作者的公钥是否有效。如果您通过受信任的渠道从作者处单独获取并且信任该渠道,则可以像自签名证书一样验证。

只要您确定 Strong Naming 私钥由作者安全保管,并且您知道作者的公钥,就可以确保未被篡改(就像确保未被篡改的数字签名电子邮件那样)。顺便说一下,不要误解:引文完全正确,并且攻击者可以轻松重新签署程序集或删除现有签名。然而,生成的程序集将具有*不同*的数字签名,可以与原始签名进行比对(如果您拥有原始公钥)。

在这种情况下,它类似于自签名证书。如果您可以以某种方式确信作者的公钥,则可以验证其真实性。但与依赖证书颁发机构的 Authenticode 不同,没有直接、系统定义的方法来分发公钥。


但是验证公钥是否有效必须手动进行,对吗? - Pablo Marambio
是的,我们在理论上讨论这个事实。实际上,不建议为这些目的使用强名称。毕竟,它只是一个强名称,而不是强签名。但从理论上讲,这是一种可能且相当安全的实现方式,就像 GPG 签名之类的东西。 - Mehrdad Afshari

1

我认为强名称对于版本控制很有用,并且可以用于帮助设置代码访问安全的信任级别。编辑:但是你不能使用它们来验证程序集是否由可信任的人或特定作者编写。请参见@RoadWarrior和@RoadWarrior在此问题上的回答。

强命名您的程序集并不能使其防篡改。
编辑:请参见@RoadWarrior和@divo的评论。如果您的应用程序正在检查程序集作者的原始私钥并强制执行强名称验证,则我的说法是不正确的。但是,如果攻击者可以访问您应用程序中的所有程序集和/或您正在使用CLR提供的开箱即用的强名称验证,则我仍然坚持我的观点。

这可以被决心攻击者破坏。

我之前阅读了一篇讨论关于强名称和安全性的文章,这让我相信对于我们所制作的程序集来说,数字签名是向我们的客户保证该程序集来自可信来源的更好选择。


2
强命名程序集并不能使其防篡改。我认为这不是真的,当你知道(并检查)原始作者的公钥时,因为篡改后的程序集将有一个不同的签名。值得信赖的使用.NET编写的MS-Office插件就依赖于这一点。 - Dirk Vollmar
1
Hamish,你可以使用强名称来验证程序集是由特定作者创建的,因为尽管攻击者可能会篡改程序集,但他无法重用原始程序集的强名称。 - HTTP 410

0

我相信有一种方法可以使用强名称来实现“信任”的目的。我了解微软只推荐使用强名称来确保程序集内容未被修改,并建议使用“Authenticode”来进行信任验证。

但是,如果加载程序(即加载这些程序集/程序的应用程序)维护一个加密的“程序集”列表,它可以加载;那么这不就解决了“信任”问题吗?

例如,包装程序可以通过公钥维护程序集名称,并通过完整的程序集名称加载程序集/程序。


0

如果您想要完整性检查您的程序集,您必须进行公钥令牌检查(请参见上面“HTTP 410”的答案)和使用StrongNameSignatureVerificationEx进行检查(https://learn.microsoft.com/de-de/dotnet/framework/unmanaged-api/strong-naming/strongnamesignatureverificationex-function)。 StrongNameSignatureVerificationEx的结果和输出参数pfWasVerified都必须为true。 只需使用一个签名密钥文件(.snk)即可用于所有程序集。

更好的方法是使用像BabelFor.Net这样的.NET程序集混淆器。其中许多都具有内置的反篡改检查功能。

干杯


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接