# Encryption Vulnerabilities
Encryption powers the modern internet, allowing secure storage of data and transmission of secrets across a network in such a way they cannot be inspected or tampered with by a third party. It is important that you use *strong encryption* algorithms when securing data in this way, or an attacker will be able to backwards engineer confidential information if they possess enough computing power.
## Encryption Algorithms
Encryption algorithms are designed to encode the contents of a string or binary object in such away that an attacker who does not possession the decryption key will not be able to decipher the contents. *Cryptography* – the study of encryption algorithms – is a complex field, and new encryption algorithms are invented by mathematicians frequently.
As computing power gets cheaper, it becomes easier for an attacker to use brute-force to decrypt the contents of encrypted data. For this reason, it’s important to use modern encryption algorithms in your code, so your secrets remain safe.
Consider the following Python function, that uses a weak encryption algorithm (MD5) to encrypt passwords. Since the algorithm does not strongly encrypt the data – it is computationally cheap to calculate the MD5 value – an attacker can quickly calculate the MD5 values of commonly used passwords, compare them to the encrypted value, and backwards engineer the passwords:
“`python import hashlibdef encrypt_password(password): return hashlib.md5(password.encode(‘utf-8’)).hexdigest() “` |
Here are some algorithms to avoid, since they provide weak encryption:
* `MD5` * `SHA-1` * `DES/CBC/PKCS5Padding` * `DES/CBC/PKCS5PADDING` * `DES/ECB/PKCS5Padding` * `AES/ECB/NoPadding` |
## Encoding is Not Encryption
Encodings like Base64 are a common method of transforming binary data to text before transmission. Anybody can decode Base64 text, however – do not depend on encoding methods to hide secrets from attackers!
## Don’t Invent Your Own Encryption Algorithms
Avoid the temptation to roll your own encryption algorithm. Cryptography is hard, and designing your own cryptographic schemes when well-studied encryption algorithms are freely available is asking for trouble.
## Symmetric vs Asymmetric Encryption Algorithms
An encryption algorithm that uses the same key to encrypt and decrypt data is called a *symmetric algorithm*. An *asymmetric algorithm* uses different keys to encrypt and decrypt data, and has the advantage that the encryption key can be made public, allowing anyone to securely encrypt data in a way that only the holder of the decryption key can read. For this reason, asymmetric algorithms are commonly called **public-key** algorithms. If you wish to securely retrieve data from a third party, always choose a public key algorithm.
## Weak Hashes
A *hash* is a type of encryption algorithm that produces an output of fixed length regardless of the input. Hash algorithms are often used to store passwords securely in a database, or generate digital signatures which prove data has not been tampered with. It is important that you use *strong hash* algorithms when securing data in this way – and include an element of randomness called a *salt* – or an attacker will be able to backwards engineer confidential information if they possess enough computing power. This often entails choosing a *one-way hash* algorithm that is
computationally infeasible to reverse.
Consider the following function that uses a strong, one-way hash called `BCrypt` to encodes a passwords when it is set, and repeats the calculation when a user attempts to reauthenticate:
“`python import [email protected](‘/signup’, methods=(‘POST’,)) def do_signup(): username = request.form[‘username’] password = request.form[‘password’]# The user is signing up – calculated their password hash and save it to the database. salt = bcrypt.gensalt() hashed = bcrypt.hashpw(password, salt) save_credentials(username, hashed) return redirect(‘/login’) @app.route(‘/login’, methods=(‘POST’,)) # The user is logging back in – recalculate their password hash and # Calculate the password hash regardless of whether the username exists, return render_template(‘login.html’, error=’Invalid username or password’) |
Since the password is strongly encrypted with a one-way hash, and generated with a salt, an attacker who manages to steal the contents of your database will still not be able to backwards engineer the credentials for your users.
## Weak Randomness
Encryption algorithms rely on random number generators to avoid being predictable. If your encryption code uses a weak source of randomness – that is, if an attacker can narrow down the range of numbers the random number generator will be outputting in a given timeframe – a malicious user has a much smaller set of combinations to attempt when trying to decrypt your sensitive data.
In Python, the easiest way to generate a random number is the `random` module, which will generate random numbers on demand. However, these random numbers are **not** cryptographically secure – since they rely on the Mersenne Twister algorithm, which is completely deterministic.
Instead, you should use the `secrets` module if you require a source of randomness when encrypting data. Use the following functions:
* The `secrets.token_hex(…)` if you want a random string of a given length.
* The `secrets.randbelow(0, n)` if you want a random number in a range.
* The `secrets.token_bytes(…)` if you need bytes to to generate cryptographic keys.
## CWEs
* [CWE-326](https://cwe.mitre.org/data/definitions/326.html)
* [CWE-328](https://cwe.mitre.org/data/definitions/328.html)
* [CWE-759](https://cwe.mitre.org/data/definitions/759.html)
* [CWE-330](https://cwe.mitre.org/data/definitions/330.html)