我问自己,在asp.net页面的代码隐藏中使用包含HMACSHA1实例的静态(共享)变量是否有危险。问题是当在同一个asp.net页面上处理多个同时请求时,所有asp.net worker-process'线程将使用相同的HMACSHA1实例。 all(HMACSHA1)instance-和ComputeHash() - 由ComputeHash()使用/修改的方法变量将被所有线程共享(=可以修改)?这个假设是正确的吗? 因此,ComputeHash的返回值不能保证是正确的?!?! 因此我不允许在所有的asp.net-threads上使用静态/共享HMACSHA1实例..
我只是想知道你对这个问题的看法。
唯一的解决办法就是在ComputeHash()方法中使用关键路径等。但这是“我们无法接触到的”..
的问候, 克里斯
答案 0 :(得分:8)
我刚从SHA256Cng.ComputeHash获得了一个未知的加密异常,当在四核HT cpu上运行8或16个并行任务时,执行散列计算。
围绕ComputeHash添加锁定语义解决了这个问题 - 所以看起来至少SHA256Cng版本不是线程安全的。
答案 1 :(得分:5)
散列算法是确定性的,它们必须每次都为给定的输入返回相同的散列。
只要您每次都使用相同的密钥,就不需要将它们设置为静态。但是,如果您使用无参数构造函数构造HMACSHA1实例,则它会生成随机密钥。您应该从KeyValue属性中获取随机值并将其与散列一起存储。
使用静态实例绝对危险。如果Thread1设置要计算的值,然后Thread2在Thread1调用ComputerHash()之前设置该值,则Thread1将获取Thread2值的散列值。如果任一线程正在设置密钥,也会发生同样的情况。
答案 2 :(得分:4)
值得知道KeyedHashAlgorithm.ComputeHash()
不是线程安全的,因为它为同一KeyedHashAlgorithm.Key
提供了非确定性结果。
在我的情况下,我想缓存KeyedHashAlgorithm,因为我的KeyedHashAlgorithm.Key
总是相同的,以验证来自客户端的真实性。我意识到ComputeHash()
不一致,可能是它将内部变量缓存到KeyedHashAlgorithm
实例中。我应该为每个线程ThreadStatic
或ThreadLocal
缓存实例。这是测试:
静态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);
此代码可用于测试“线程安全”结果的其他函数。希望这会对其他人有所帮助。
答案 3 :(得分: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;
}
}
现在,两个旁注:
访问线程静态字段所需的时间比访问正常的静态字段要长得多。因此,线程静态版本对您来说可能更好,也可能不是更好。
如果您每页请求一次这样做,那么差异将是如此微小,以至于您选择哪种方法无关紧要。如果你是在一个非常紧凑的循环中执行此操作,或者锁定部分中的代码需要很长时间,那么选择可能很重要。