7

我在问自己,在 asp.net 页面的代码隐藏中使用包含 HMACSHA1 实例的静态(共享)变量是否会很危险。问题是在处理同一个 asp.net 页面上的多个同时请求时,所有 asp.net worker-process 线程将使用相同的 HMACSHA1-instance。由 ComputeHash() 使用/修改的所有 (HMACSHA1) 实例和 ComputeHash() 方法变量将由所有线程共享(= 可以修改)?!这个假设正确吗?结果 ComputeHash 的返回值不能保证是正确的?!?!因此,我不允许在所有 asp.net 线程上使用静态/共享 HMACSHA1 实例。

我只是想知道你对这个问题的看法。

对此的唯一解决方案是 ComputeHash() 方法中的关键路径等。但那是“我们无法企及的”..

问候,克里斯

4

4 回答 4

9

在四核 HT cpu 上运行 8 或 16 个并行任务时,我刚刚从 SHA256Cng.ComputeHash 中得到一个未知的加密异常,其中包括执行哈希计算。

在 ComputeHash 周围添加锁定语义解决了这个问题 - 所以看起来至少 SHA256Cng 版本不是线程安全的。

于 2011-11-13T23:27:02.807 回答
6

散列算法是确定性的,它们每次都必须为给定的输入返回相同的散列。

只要您每次都使用相同的密钥,就无需将它们设为静态。但是,如果您使用无参数构造函数构造 HMACSHA1 实例,那么它会生成一个随机密钥。您应该从 KeyValue 属性中获取随机值并将其与哈希一起存储。

使用静态实例绝对是危险的。如果 Thread1 设置要计算的值,然后 Thread2 在 Thread1 调用 ComputerHash() 之前设置该值,则 Thread1 将获取 Thread2 值的哈希值。如果任一线程正在设置密钥,也会发生同样的情况。

于 2009-02-20T14:22:02.340 回答
6

值得知道的KeyedHashAlgorithm.ComputeHash()是,这不是线程安全的,因为它给出了相同的不确定结果KeyedHashAlgorithm.Key

在我的情况下,我想缓存 KeyedHashAlgorithm 因为我KeyedHashAlgorithm.Key的总是相同的,以从客户端验证真实性。我意识到这ComputeHash()并不一致,可能它会将内部变量缓存到KeyedHashAlgorithm实例中。我应该缓存每个线程的实例ThreadStaticThreadLocal. 这是测试:

静态KeyedHashAlgorithm给出不一致的结果:

var kha = KeyedHashAlgorithm.Create("HMACSHA256");
kha.Key = Encoding.UTF8.GetBytes("key");
Action comp = () =>
{
    var computed = kha.ComputeHash(Encoding.UTF8.GetBytes("message"));
    Console.WriteLine(Convert.ToBase64String(computed));
};
Parallel.Invoke(comp, comp, comp, comp, comp, comp, comp, comp);

KeyedHashAlgorithm每个线程相比:

ThreadLocal<KeyedHashAlgorithm> tl= new ThreadLocal<KeyedHashAlgorithm>(() =>
{
    var kha = KeyedHashAlgorithm.Create("HMACSHA256");
    kha.Key = Encoding.UTF8.GetBytes("key");
    return kha;
});
Action comp = () =>
{
    var computed = tl.Value.ComputeHash(Encoding.UTF8.GetBytes("message"));
    Console.WriteLine(Convert.ToBase64String(computed));
};
Parallel.Invoke(comp, comp, comp, comp, comp, comp, comp, comp);

此代码可用于测试“线程安全”结果的其他功能。希望这对其他人有帮助。

于 2014-11-11T07:21:28.803 回答
1

如果你想要线程安全而不需要锁定,你可以使用 ThreadStatic 属性在每个线程上创建一个唯一的实例,如下所示:

[ThreadStatic]
private static HMACSHA1 _hmacSha1;

public static HMACSHA1 HmacSha1
{
    get 
    {
        if (_hmacSha1 == null)
        {
            // this will happen once on each thread
            _hmacSha1 = new HMACSHA1(GetKeyBytes());
        }               

        return _hmacSha1;
    }
}

现在,两个旁注:

  1. 访问线程静态字段比访问普通静态字段花费的时间要长得多。因此,线程静态版本可能对您更好,也可能不会更好。

  2. 如果您在每个页面请求中执行一次,那么差异将非常小,以至于您选择哪种方法都无关紧要。如果您在一个非常紧凑的循环中执行此操作,或者您的锁定部分中的代码花费了很长时间,那么选择可能很重要。

于 2014-10-03T17:46:33.190 回答