Use of an inappropriate cryptographic hashing algorithm on passwords¶
ID: swift/weak-password-hashing
Kind: path-problem
Security severity: 7.5
Severity: warning
Precision: high
Tags:
- security
- external/cwe/cwe-327
- external/cwe/cwe-328
- external/cwe/cwe-916
Query suites:
- swift-code-scanning.qls
- swift-security-extended.qls
- swift-security-and-quality.qls
Click to see the query in the CodeQL repository
Hash functions that are not sufficiently computationally hard can leave data vulnerable. You should not use such functions for password hashing.
A strong cryptographic hash function should be resistant to:
Pre-image attacks. If you know a hash value
h(x)
, you should not be able to easily find the inputx
.Collision attacks. If you know a hash value
h(x)
, you should not be able to easily find a different inputy
with the same hash valueh(x) = h(y)
.Brute force. If you know a hash value
h(x)
, you should not be able to find an inputy
that computes to that hash value using brute force attacks without significant computational effort. All of MD5, SHA-1, SHA-2 and SHA-3 are weak against offline brute forcing, since they are not sufficiently computationally hard. This includes SHA-224, SHA-256, SHA-384 and SHA-512, which are in the SHA-2 family.
Password hashing algorithms should be slow and/or memory intensive to compute, to make brute force attacks more difficult.
Recommendation¶
For password storage, you should use a sufficiently computationally hard cryptographic hash function, such as one of the following:
Argon2
scrypt
bcrypt
PBKDF2
Example¶
The following examples show two versions of the same function. In both cases, a password is hashed using a cryptographic hashing algorithm. In the first case, the SHA-512 hashing algorithm is used. It is vulnerable to offline brute force attacks:
let passwordData = Data(passwordString.utf8)
let passwordHash = Crypto.SHA512.hash(data: passwordData) // BAD: SHA-512 is not suitable for password hashing.
// ...
if Crypto.SHA512.hash(data: Data(passwordString.utf8)) == passwordHash {
// ...
}
Here is the same function using Argon2, which is suitable for password hashing:
import Argon2Swift
let salt = Salt.newSalt()
let result = try! Argon2Swift.hashPasswordString(password: passwordString, salt: salt) // GOOD: Argon2 is suitable for password hashing.
let passwordHash = result.encodedString()
// ...
if try! Argon2Swift.verifyHashString(password: passwordString, hash: passwordHash) {
// ...
}
References¶
OWASP: Password Storage Cheat Sheet
GitHub: CryptoSwift README - Password-Based Key Derivation Function
libsodium: libsodium bindings for other languages
GitHub: Argon2Swift
Common Weakness Enumeration: CWE-327.
Common Weakness Enumeration: CWE-328.
Common Weakness Enumeration: CWE-916.