CodeQL 文档

静态 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

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

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

参考

  • ©GitHub 公司
  • 条款
  • 隐私