CodeQL 文档

非线程安全地捕获 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

点击查看 CodeQL 代码库中的查询

实现 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();
}

参考

  • ©GitHub 公司
  • 条款
  • 隐私