Don't use weak hash algorithms (MD5, SHA1)

Rule ND3109 Security

Matches methods calling MD5, SHA1, RIPEMD160 or HMACMD5 constructors, factories (MD5.Create()), one-shot helpers (SHA1.HashData()) or selecting them through HashAlgorithmName.MD5 / HashAlgorithmName.SHA1. Practical collision attacks exist against MD5 (since 2004) and SHA1 (SHAttered, 2017): https://shattered.io/ . NIST deprecated SHA1 for digital signatures in 2011 and plans its full retirement by 2030.

This is CWE-328 Use of Weak Hash.

Flagged Code
static string Fingerprint(byte[] data) {
   using var md5 = MD5.Create();                                // flagged: MD5
   return Convert.ToHexString(md5.ComputeHash(data));
}
static byte[] Digest(byte[] data) => SHA1.HashData(data);       // flagged: SHA1
Fixed Code
static string Fingerprint(byte[] data) {
   using var sha256 = SHA256.Create();
   return Convert.ToHexString(sha256.ComputeHash(data));
}
static byte[] Digest(byte[] data) => SHA256.HashData(data);

How to fix

Use SHA256 or stronger (SHA384, SHA512, SHA3_256) for integrity and signatures, and a dedicated password hashing function (Rfc2898DeriveBytes with SHA256, bcrypt, scrypt or Argon2) for passwords.

When the hash is not security-related (cache keys, ETags, deduplication of non-hostile data), prefer a fast non-cryptographic hash such as System.IO.Hashing.XxHash3 and suppress the issue.

CQLinq source

This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.

// <Name>Don't use weak hash algorithms (MD5, SHA1)</Name>
// <Id>ND3109:DontUseWeakHashAlgorithms</Id>
warnif count > 0

// Third-party members are part of the code model only when the application uses them.
let weakHashTypes = ThirdParty.Types.WithFullNameIn(
  "System.Security.Cryptography.MD5",
  "System.Security.Cryptography.MD5CryptoServiceProvider",
  "System.Security.Cryptography.MD5Cng",
  "System.Security.Cryptography.SHA1",
  "System.Security.Cryptography.SHA1Managed",
  "System.Security.Cryptography.SHA1CryptoServiceProvider",
  "System.Security.Cryptography.SHA1Cng",
  "System.Security.Cryptography.RIPEMD160",
  "System.Security.Cryptography.RIPEMD160Managed",
  "System.Security.Cryptography.HMACMD5",
  "System.Security.Cryptography.HMACRIPEMD160",
  "System.Security.Cryptography.MACTripleDES")

// Constructors, factories and one-shot helpers: new MD5CryptoServiceProvider(), SHA1.Create(), MD5.HashData(bytes)...
let weakHashMembers = weakHashTypes.ChildMethods()
   .Where(m => m.IsConstructor ||
              (m.IsStatic && m.SimpleName.StartsWithAny("Create", "HashData", "TryHashData", "HashDataAsync")))

   // HashAlgorithmName.MD5 / HashAlgorithmName.SHA1 select a weak algorithm in
   // IncrementalHash, Rfc2898DeriveBytes, RSA.SignData(), ECDsa.SignData(), X509 requests...
   .Concat(ThirdParty.Types.WithFullName("System.Security.Cryptography.HashAlgorithmName")
      .ChildMethods().Where(m => m.SimpleNameEqualsAny("get_MD5", "get_SHA1")))
   .ToHashSetEx()

where weakHashMembers.Any()

from m in Application.Methods.UsingAny(weakHashMembers)
let weakHashMembersUsed = m.MethodsCalled.Intersect(weakHashMembers).ToArray()
select new {
  m,
  weakHashMembersUsed,
  Debt = (20 + 10 * weakHashMembersUsed.Length).ToMinutes().ToDebt(),
  Severity = Severity.High
}

//<Expl>
// The *{0}* uses the weak hash algorithm(s) *{1}*.
// MD5 and SHA1 are vulnerable to collision attacks and must not protect
// passwords, signatures, tokens or integrity checks.
//</Expl>

//<Desc>
// Matches methods calling MD5, SHA1, RIPEMD160 or HMACMD5 constructors,
// factories (*MD5.Create()*), one-shot helpers (*SHA1.HashData()*) or
// selecting them through *HashAlgorithmName.MD5* / *HashAlgorithmName.SHA1*.
// Practical collision attacks exist against MD5 (since 2004) and SHA1
// (SHAttered, 2017): https://shattered.io/ . NIST deprecated SHA1 for
// digital signatures in 2011 and plans its full retirement by 2030.
//
// This is CWE-328 *Use of Weak Hash*.
//
// **Flagged Code**
// ```csharp
// static string Fingerprint(byte[] data) {
//    using var md5 = MD5.Create();                                // flagged: MD5
//    return Convert.ToHexString(md5.ComputeHash(data));
// }
// static byte[] Digest(byte[] data) => SHA1.HashData(data);       // flagged: SHA1
// ```
//
// **Fixed Code**
// ```csharp
// static string Fingerprint(byte[] data) {
//    using var sha256 = SHA256.Create();
//    return Convert.ToHexString(sha256.ComputeHash(data));
// }
// static byte[] Digest(byte[] data) => SHA256.HashData(data);
// ```
//</Desc>

//<HowToFix>
// Use *SHA256* or stronger (*SHA384*, *SHA512*, *SHA3_256*) for integrity
// and signatures, and a dedicated password hashing function (*Rfc2898DeriveBytes
// with SHA256*, *bcrypt*, *scrypt* or *Argon2*) for passwords.
//
// When the hash is not security-related (cache keys, ETags, deduplication
// of non-hostile data), prefer a fast non-cryptographic hash such as
// *System.IO.Hashing.XxHash3* and suppress the issue.
//</HowToFix>

Security: all 39 items