非线程安全地捕获 ICryptoTransform 对象¶
ID: cs/thread-unsafe-icryptotransform-captured-in-lambda
Kind: problem
Security severity: 7.0
Severity: warning
Precision: medium
Tags:
- concurrency
- security
- external/cwe/cwe-362
Query suites:
- csharp-security-extended.qls
- csharp-security-and-quality.qls
实现 System.Security.Cryptography.ICryptoTransform
的类不是线程安全的。
此问题是由这些类使用 Microsoft CAPI/CNG 模式实现的方式导致的。
例如,当哈希类实现此接口时,通常会创建一个特定于实例的哈希对象(例如,使用 BCryptCreateHash
函数)。可以多次调用此对象以将数据添加到哈希中(例如,BCryptHashData
)。最后,调用一个函数来完成哈希并返回数据(例如,BCryptFinishHash
)。
允许在调用完成函数之前,使用来自多个线程的数据调用同一个哈希对象,可能会导致结果不正确。
例如,如果您有多个线程在静态哈希对象上对 "abc"
进行哈希运算,则有时可能会获得对 "abcabc"
进行哈希运算的结果(不正确),或者遇到其他意外行为。
Microsoft 以外的人不太可能编写实现 ICryptoTransform
的类,即使他们这样做,他们也可能会遵循与实现此接口的现有类相同的常见模式。
任何实现 System.Security.Cryptography.ICryptoTransform
的对象都不应在并发线程中使用,因为此类对象的实例成员也不是线程安全的。
潜在问题一开始可能并不明显,但范围可能从显式错误(如异常)到在多个线程中共享此类对象的实例时的结果不正确。
建议¶
创建实现 System.Security.Cryptography.ICryptoTransform
或具有该类型字段的对象的新实例,以避免在多个线程之间共享它。
示例¶
此示例演示了以生成错误结果或可能引发异常的方式使用共享的 System.Security.Cryptography.ICryptoTransform
的危险。
public static void RunThreadUnSafeICryptoTransformLambdaBad()
{
const int threadCount = 4;
// This local variable for a hash object is going to be shared across multiple threads
var sha1 = SHA1.Create();
var b = new Barrier(threadCount);
Action start = () => {
b.SignalAndWait();
for (int i = 0; i < 1000; i++)
{
var pwd = Guid.NewGuid().ToString();
var bytes = Encoding.UTF8.GetBytes(pwd);
// This call may fail, or return incorrect results
sha1.ComputeHash(bytes);
}
};
var threads = Enumerable.Range(0, threadCount)
.Select(_ => new ThreadStart(start))
.Select(x => new Thread(x))
.ToList();
foreach (var t in threads) t.Start();
foreach (var t in threads) t.Join();
}
一个简单的解决方法是将 lambda 捕获的局部变量 sha1
更改为 lambda 内部的局部变量。
public static void RunThreadUnSafeICryptoTransformLambdaFixed()
{
const int threadCount = 4;
var b = new Barrier(threadCount);
Action start = () => {
b.SignalAndWait();
// The hash object is no longer shared
for (int i = 0; i < 1000; i++)
{
var sha1 = SHA1.Create();
var pwd = Guid.NewGuid().ToString();
var bytes = Encoding.UTF8.GetBytes(pwd);
sha1.ComputeHash(bytes);
}
};
var threads = Enumerable.Range(0, threadCount)
.Select(_ => new ThreadStart(start))
.Select(x => new Thread(x))
.ToList();
foreach (var t in threads) t.Start();
foreach (var t in threads) t.Join();
}
参考¶
Microsoft 文档,ThreadStaticAttribute 类。
Stack Overflow,为什么 SHA1.ComputeHash 在高负载和多线程情况下会失败?。
常见弱点枚举:CWE-362。