静态 ICryptoTransform 字段的线程不安全使用¶
ID: cs/thread-unsafe-icryptotransform-field-in-class
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
类型的非静态对象。
或者,您也可以考虑使用 ThreadStatic
属性,但请确保您已阅读文档中的初始化说明。
示例¶
此示例演示了以生成错误结果的方式使用静态 System.Security.Cryptography.ICryptoTransform
的危险。
internal class TokenCacheThreadUnsafeICryptoTransformDemo
{
private static SHA256 _sha = SHA256.Create();
public string ComputeHash(string data)
{
byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(data);
return Convert.ToBase64String(_sha.ComputeHash(passwordBytes));
}
}
class Program
{
static void Main(string[] args)
{
int max = 1000;
Task[] tasks = new Task[max];
Action<object> action = (object obj) =>
{
var unsafeObj = new TokenCacheThreadUnsafeICryptoTransformDemo();
if (unsafeObj.ComputeHash((string)obj) != "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=")
{
Console.WriteLine("**** We got incorrect Results!!! ****");
}
};
for (int i = 0; i < max; i++)
{
// hash calculated on all threads should be the same:
// ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0= (base64)
//
tasks[i] = Task.Factory.StartNew(action, "abc");
}
Task.WaitAll(tasks);
}
}
一个简单的修复方法是通过删除 static
关键字将 _sha
字段从静态成员更改为实例成员。
internal class TokenCacheThreadUnsafeICryptoTransformDemoFixed
{
// We are replacing the static SHA256 field with an instance one
//
//private static SHA256 _sha = SHA256.Create();
private SHA256 _sha = SHA256.Create();
public string ComputeHash(string data)
{
byte[] passwordBytes = UTF8Encoding.UTF8.GetBytes(data);
return Convert.ToBase64String(_sha.ComputeHash(passwordBytes));
}
}
class Program
{
static void Main(string[] args)
{
int max = 1000;
Task[] tasks = new Task[max];
Action<object> action = (object obj) =>
{
var safeObj = new TokenCacheThreadUnsafeICryptoTransformDemoFixed();
if (safeObj.ComputeHash((string)obj) != "ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0=")
{
Console.WriteLine("**** We got incorrect Results!!! ****");
}
};
for (int i = 0; i < max; i++)
{
// hash calculated on all threads should be the same:
// ungWv48Bz+pBQUDeXa4iI7ADYaOWF3qctBD/YfIAFa0= (base64)
//
tasks[i] = Task.Factory.StartNew(action, "abc");
}
Task.WaitAll(tasks);
}
}
参考¶
Microsoft 文档,ThreadStaticAttribute 类。
Stack Overflow,为什么 SHA1.ComputeHash 在高负载和多线程情况下会失败?。
常见弱点枚举:CWE-362。