[teamai] Push 87 resource(s) from XingfenD
This commit is contained in:
@@ -0,0 +1,424 @@
|
||||
# Cryptography Security Rules
|
||||
|
||||
Cryptography vulnerabilities threaten confidentiality and integrity of sensitive data.
|
||||
|
||||
**Rules:**
|
||||
|
||||
1. TLS MUST use 1.2+.
|
||||
2. NEVER use DES, RC4, MD5, or SHA1 for security purposes.
|
||||
3. SSH host keys MUST be verified — NEVER use `InsecureIgnoreHostKey`.
|
||||
4. Passwords MUST be hashed with Argon2id (preferred) or bcrypt.
|
||||
5. Security-critical randomness MUST use `crypto/rand`.
|
||||
|
||||
---
|
||||
|
||||
## Algorithm Selection Guide
|
||||
|
||||
Choose the right algorithm for the job — using the wrong primitive (e.g. SHA256 for passwords) is as dangerous as using a broken one:
|
||||
|
||||
| Use Case | Recommended | Avoid | Why |
|
||||
| --- | --- | --- | --- |
|
||||
| Symmetric encryption | AES-256-GCM, ChaCha20-Poly1305 | DES, 3DES, AES-ECB, RC4 | ECB reveals patterns; DES/RC4 are broken |
|
||||
| Password hashing | Argon2id (preferred), bcrypt, scrypt | MD5, SHA-1, plain SHA-256 | Fast hashes enable brute-force; memory-hard functions resist GPU attacks |
|
||||
| Message authentication | HMAC-SHA256, Poly1305 | HMAC-MD5, HMAC-SHA1 | MD5/SHA1 have known collision weaknesses |
|
||||
| Digital signatures | Ed25519, ECDSA P-256 | RSA-PKCS1v1.5 | PKCS1v1.5 has padding oracle vulnerabilities |
|
||||
| Key exchange | X25519, ECDH P-256 | Static RSA key transport | Forward secrecy requires ephemeral keys |
|
||||
| Random generation | `crypto/rand` | `math/rand` | `math/rand` output is predictable |
|
||||
| TLS | TLS 1.2+ (prefer 1.3) | TLS 1.0, 1.1, SSL | Known attacks (BEAST, POODLE) on older versions |
|
||||
|
||||
### Key Size Requirements
|
||||
|
||||
| Algorithm | Minimum Key Size | Recommended |
|
||||
| --------- | ------------------------ | ---------------- |
|
||||
| RSA | 2048 bits | 4096 bits |
|
||||
| AES | 128 bits | 256 bits |
|
||||
| ECDSA | P-256 (128-bit security) | P-256 or Ed25519 |
|
||||
|
||||
---
|
||||
|
||||
## Key Rotation Pattern
|
||||
|
||||
Keys should be rotated periodically. Use envelope encryption so rotating the Key Encryption Key (KEK) doesn't require re-encrypting all data:
|
||||
|
||||
```go
|
||||
// Envelope encryption: encrypt data with a DEK, encrypt DEK with KEK
|
||||
func EnvelopeEncrypt(kek, plaintext []byte) (encryptedDEK, ciphertext []byte, err error) {
|
||||
// 1. Generate random Data Encryption Key
|
||||
dek := make([]byte, 32)
|
||||
if _, err := rand.Read(dek); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 2. Encrypt data with DEK
|
||||
ciphertext, err = EncryptAESGCM(dek, plaintext)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// 3. Encrypt DEK with KEK
|
||||
encryptedDEK, err = EncryptAESGCM(kek, dek)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return encryptedDEK, ciphertext, nil
|
||||
}
|
||||
|
||||
func EnvelopeDecrypt(kek, encryptedDEK, ciphertext []byte) ([]byte, error) {
|
||||
dek, err := DecryptAESGCM(kek, encryptedDEK)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return DecryptAESGCM(dek, ciphertext)
|
||||
}
|
||||
|
||||
func EncryptAESGCM(key, plaintext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil { return nil, err }
|
||||
aead, err := cipher.NewGCM(block)
|
||||
if err != nil { return nil, err }
|
||||
nonce := make([]byte, aead.NonceSize())
|
||||
if _, err := rand.Read(nonce); err != nil { return nil, err }
|
||||
return aead.Seal(nonce, nonce, plaintext, nil), nil
|
||||
}
|
||||
|
||||
func DecryptAESGCM(key, ciphertext []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil { return nil, err }
|
||||
aead, err := cipher.NewGCM(block)
|
||||
if err != nil { return nil, err }
|
||||
nonceSize := aead.NonceSize()
|
||||
if len(ciphertext) < nonceSize {
|
||||
return nil, errors.New("ciphertext too short")
|
||||
}
|
||||
return aead.Open(nil, ciphertext[:nonceSize], ciphertext[nonceSize:], nil)
|
||||
}
|
||||
```
|
||||
|
||||
When the KEK is rotated, only re-encrypt the DEKs (small), not the data (potentially large).
|
||||
|
||||
---
|
||||
|
||||
## Common Cryptographic Mistakes
|
||||
|
||||
### Mistake 1: AES-ECB reveals patterns — High
|
||||
|
||||
ECB encrypts each block independently — identical plaintext blocks produce identical ciphertext blocks, revealing data structure:
|
||||
|
||||
```go
|
||||
// Bad — ECB mode reveals patterns in structured data
|
||||
block, _ := aes.NewCipher(key)
|
||||
// Using block.Encrypt directly = ECB mode
|
||||
|
||||
// Good — GCM provides authenticated encryption
|
||||
aead, err := cipher.NewGCM(block) // randomized, authenticated
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nonce := make([]byte, aead.NonceSize())
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ciphertext := aead.Seal(nonce, nonce, plaintext, nil)
|
||||
```
|
||||
|
||||
### Mistake 2: Reusing nonces — Critical
|
||||
|
||||
A nonce reuse with AES-GCM completely breaks confidentiality and authentication:
|
||||
|
||||
```go
|
||||
// Bad — static or reused nonce
|
||||
nonce := []byte("fixed_nonce!") // catastrophic with GCM
|
||||
|
||||
// Good — random nonce per encryption
|
||||
nonce := make([]byte, 12) // 96-bit for GCM
|
||||
if _, err := rand.Read(nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
```
|
||||
|
||||
### Mistake 3: Non-constant-time comparison for secrets — Medium
|
||||
|
||||
Comparing secrets with `==` short-circuits on the first differing byte, leaking timing information. See [Network/Web Security — Observable Timing](./network.md) for constant-time comparison patterns using `crypto/subtle`.
|
||||
|
||||
---
|
||||
|
||||
## Insecure TLS Configuration — High
|
||||
|
||||
Using insecure TLS configurations can expose your application to man-in-the-middle attacks.
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
transport := &http.Transport{
|
||||
TLSClientConfig: &tls.Config{
|
||||
InsecureSkipVerify: true, // DON'T: verify certificates
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
import "crypto/tls"
|
||||
|
||||
func secureConfig() *tls.Config {
|
||||
return &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## DES Encryption — High
|
||||
|
||||
DES is cryptographically broken.
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
import "crypto/des"
|
||||
block, _ := des.NewCipher(key) // DON'T: broken
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
import "crypto/aes"
|
||||
block, _ := aes.NewCipher(key) // OK: AES
|
||||
cipher.NewGCM(block) // OK: GCM for auth
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Insecure SSH Host Key Verification — High
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
import "golang.org/x/crypto/ssh"
|
||||
&ssh.ClientConfig{
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(), // DON'T
|
||||
}
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
import "golang.org/x/crypto/ssh"
|
||||
&ssh.ClientConfig{
|
||||
HostKeyCallback: ssh.FixedHostKey(publicKey),
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## MD5 Hash — High
|
||||
|
||||
MD5 is collision-prone and weak for security.
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
import "crypto/md5"
|
||||
hash := md5.Sum([]byte(data)) // DON'T: weak
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
// For password hashing:
|
||||
import "golang.org/x/crypto/argon2"
|
||||
hash := argon2.IDKey([]byte(pw), salt, 3, 64*1024, 4, 32)
|
||||
|
||||
// Or bcrypt (simpler API, no salt management):
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// For general-purpose hashing (not passwords):
|
||||
import "crypto/sha256"
|
||||
digest := sha256.Sum256(data)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## RC4 Cipher — High
|
||||
|
||||
RC4 is cryptographically broken.
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
import "crypto/rc4"
|
||||
cipher, _ := rc4.NewCipher(key) // DON'T: broken
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
import "crypto/cipher"
|
||||
import "crypto/aes"
|
||||
aead, _ := cipher.NewGCM(block) // OK: AES-GCM
|
||||
|
||||
// Or ChaCha20:
|
||||
import "golang.org/x/crypto/chacha20poly1305"
|
||||
aead, _ := chacha20poly1305.New(key)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## SHA1 Hash — Medium
|
||||
|
||||
SHA1 provides insufficient collision resistance.
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
import "crypto/sha1"
|
||||
hash := sha1.Sum(data) // DON'T: weak
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
import "crypto/sha256"
|
||||
hash := sha256.Sum256(data)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Weak Cryptographic Algorithms — Medium
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
import "crypto/hmac"
|
||||
import "crypto/md5"
|
||||
mac := hmac.New(md5.New, key) // DON'T: HMAC-MD5
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
import "crypto/sha256"
|
||||
mac := hmac.New(sha256.New, key)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Insufficient Key Strength — Medium
|
||||
|
||||
RSA keys smaller than 2048 bits are insufficient.
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
import "crypto/rsa"
|
||||
key, _ := rsa.GenerateKey(rand.Reader, 1024) // DON'T: too weak
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
key, _ := rsa.GenerateKey(rand.Reader, 4096) // OK: 2048+ bits
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Weak Random Number Generators — High
|
||||
|
||||
`math/rand` is predictable, never use for security.
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
import "math/rand"
|
||||
bytes := make([]byte, 16)
|
||||
rand.Read(bytes) // DON'T: predictable
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
import "crypto/rand"
|
||||
_, err := rand.Read(bytes) // OK: cryptographically secure
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Weak TLS Versions — High
|
||||
|
||||
TLS 1.0 and 1.1 have known vulnerabilities.
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
import "crypto/tls"
|
||||
&tls.Config{MinVersion: tls.VersionTLS10} // DON'T
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
&tls.Config{MinVersion: tls.VersionTLS12} // OK
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Password Hashing — High
|
||||
|
||||
Don't use MD5, SHA1, or single-iteration hashes for passwords.
|
||||
|
||||
**Bad:**
|
||||
|
||||
```go
|
||||
import "crypto/sha256"
|
||||
hash := sha256.Sum256([]byte(password)) // DON'T: too fast
|
||||
```
|
||||
|
||||
**Good:**
|
||||
|
||||
```go
|
||||
// Argon2id (preferred) — memory-hard, resists GPU attacks:
|
||||
import "golang.org/x/crypto/argon2"
|
||||
key := argon2.IDKey([]byte(password), salt, 3, 64*1024, 4, 32)
|
||||
|
||||
// Or bcrypt (simpler API, widely supported):
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
hash, err := bcrypt.GenerateFromPassword([]byte(pw), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Or PBKDF2 with 600,000+ iterations (Go 1.24+ stdlib):
|
||||
import "crypto/pbkdf2"
|
||||
key, err := pbkdf2.Key(sha512.New, password, salt, 600_000, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Or scrypt:
|
||||
import "golang.org/x/crypto/scrypt"
|
||||
key, err := scrypt.Key([]byte(password), salt, 32768, 8, 1, 32)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
```
|
||||
|
||||
For Go 1.24+, prefer stdlib `crypto/hkdf`, `crypto/pbkdf2`, and `crypto/sha3`. Use `golang.org/x/crypto/...` fallbacks only for modules targeting older Go versions or for algorithms still outside the standard library.
|
||||
|
||||
---
|
||||
|
||||
## CWE References
|
||||
|
||||
- **CWE-327**: Use of a Broken or Risky Cryptographic Algorithm
|
||||
- **CWE-331**: Insufficient Entropy
|
||||
- **CWE-326**: Inadequate Encryption Strength
|
||||
- **CWE-295**: Improper Certificate Validation
|
||||
- **CWE-330**: Use of Insufficiently Random Values
|
||||
- **CWE-916**: Use of Password Hash With Insufficient Computational Effort
|
||||
Reference in New Issue
Block a user