Description
Collision weaknesses in MD5 and SHA-1 allow different data to share a hash, undermining integrity and signature checks. DES's short key, 3DES's small block size and limited security margin, and RC4's keystream biases create separate encryption risks. Hash collisions and decryption attacks are different problems; choose replacements for the actual use.
Potential impact
- Collision attacks may let altered data or signatures pass verification.
- Sensitive plaintext may be exposed depending on the encryption scheme and usage volume.
- Inappropriate token authentication can allow message forgery.
Remediation
- Use SHA-2, such as SHA-256, or SHA-3 where collision resistance is needed. A plain hash cannot authenticate integrity if an attacker can replace both the data and its hash.
- Use HMAC-SHA256 with a dedicated secret key for message authentication and
hmac.Equalfor verification. Use a separate password-hashing function for password storage. - Replace DES, 3DES and RC4 with authenticated encryption such as AES-GCM. If CBC is unavoidable, use an unpredictable IV and encrypt-then-MAC with an independent key.
- Supply keys through an approved secret or key-management system instead of hardcoding them.
- Never reuse a GCM nonce with the same key. With random nonces as shown here, keep encryption operations per key at or below
2^32.
Examples
The 3DES function in the before-example is flawed encryption that only handles input lengths divisible by the block size. Other lengths may panic, and it provides no authentication.
Before
go
package insecure
import (
"crypto/des"
"crypto/md5"
"crypto/rc4"
"crypto/sha1"
)
// MD5 for file integrity checks (weak)
func CheckFileIntegrityMD5(data []byte) [16]byte {
return md5.Sum(data)
}
// SHA-1 for token authentication (weak)
func SignTokenSHA1(token string, secret []byte) []byte {
h := sha1.New()
h.Write(secret)
h.Write([]byte(token))
return h.Sum(nil)
}
// 3DES for sensitive data encryption (weak)
func EncryptWith3DES(key, plaintext []byte) ([]byte, error) {
block, err := des.NewTripleDESCipher(key)
if err != nil {
return nil, err
}
ciphertext := make([]byte, len(plaintext))
// Simplified example: direct blocks without a mode (also unsafe)
for i := 0; i < len(plaintext); i += block.BlockSize() {
block.Encrypt(ciphertext[i:], plaintext[i:])
}
return ciphertext, nil
}
// RC4 stream encryption (weak)
func EncryptWithRC4(key, plaintext []byte) ([]byte, error) {
c, err := rc4.NewCipher(key)
if err != nil {
return nil, err
}
dst := make([]byte, len(plaintext))
c.XORKeyStream(dst, plaintext)
return dst, nil
}
After
go
package secure
import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/rand"
"crypto/sha256"
"io"
"fmt"
)
// SHA-256 for file integrity checks
func CheckFileIntegritySHA256(data []byte) [32]byte {
return sha256.Sum256(data)
}
// HMAC-SHA256 for token authentication
func SignTokenHMAC(token string, secret []byte) []byte {
mac := hmac.New(sha256.New, secret)
mac.Write([]byte(token))
return mac.Sum(nil)
}
// Authenticated encryption using AES-GCM
func EncryptWithAESGCM(key, plaintext []byte) (nonce []byte, ciphertext []byte, err error) {
block, err := aes.NewCipher(key) // 16-, 24- or 32-byte key (AES-128/192/256)
if err != nil {
return nil, nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, nil, err
}
nonce = make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, nil, err
}
ciphertext = gcm.Seal(nil, nonce, plaintext, nil)
return nonce, ciphertext, nil
}
func DecryptWithAESGCM(key, nonce, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if len(nonce) != gcm.NonceSize() {
return nil, fmt.Errorf("invalid nonce length")
}
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
Explanation:
- Before: The code uses weak hashes,
SHA1(secret || token)instead of HMAC, unauthenticated 3DES block processing and RC4. Their risks differ according to collision resistance, message authentication and encryption requirements. - After: SHA-256, HMAC-SHA256 and AES-GCM serve distinct purposes. Store the nonce with the ciphertext and reject data on decryption failure. Invalid nonce lengths are rejected before
Open. The caller must manage key supply and replacement.