Certain data needs to be encrypted when in transit or at rest. For example, passwords should only be sent from the browser via HTTPS connections and, once on the server, should be hashed rather than stored in plain text. If either of these two criteria were to be neglected, this would be considered a cryptographic failure.
| id | username | password_hash |
|----|----------------|--------------------------------------------------------------|
| 1 | fluffy26 | $2a$10$qy7f0n06Xk8zA1tKQcIgReB1Xkf/JjiJU9c.mt452M9fgVm37ERwa |
| 2 | the_wizard | $2a$10$SqW62Nj6s84q5eYxmIlAyOkXZ6fzyQ2kUyumeHoO2YArQxoirXR0W |
| 3 | anonymous98483 | $2a$10$jEk07B/EQ63luT3WdOoFcOPhD2x2E4VRz4SR64dASLFO0rnQBr09q |
Even if encryption is incorporated, cryptographic failures can still occur. For example, a website storing passwords as hashes would still be cryptographically unsound if they were using the MD5 algorithm to hash passwords, since this algorithm is deemed unfit for cryptography.
An application might also be using a sound cryptographic algorithm but be mismanaging its keys. For example, an encryption key used to sign tokens might be stored directly in the source code or might lack the complexity required to be secure.
// INCORRECT: encryption key is too simple and stored in src code
jwt.sign({
data
}, 'password123', { expiresIn: '1h' });
// CORRECT: encryption key is injected as environmental variable
jwt.sign({
data
}, process.env.JWT_PASSWORD, { expiresIn: '1h' });
Cryptographic failures can come in a variety of forms. For example, password generators rely on random-number-generators. If these random-number-generators are not cryptographically sound, the passwords themselves will be vulnerable. This article describes a recent example from Kaspersky's password generator.
An important side-note here is that you should always use modern, battle-tested software for cryptography. For example, you should use a library like npm's Bcrypt to hash passwords rather than try and design or implement your own solution. It can be tempting to think that your application will be more secure if you invent your own cryptographic solutions - since hackers will be unfamiliar with it - however this is widely considered to be false.
The idea dates back to the 19th century with Dutch cryptographer Auguste Kerckhoffs
The principle holds that a cryptosystem should be secure, even if everything about the system, except the key, is public knowledge.
Essentially, you shouldn't rely on the fact that hackers aren't familiar with how your security works; it's better to use a robust and ubiquitous algorithm than a designed-in-house flawed one. This is why open-source software can still be secure.
That being said, in practical terms, it's still better to minimise the amount of information hackers are privy to. If they have to first workout which framework, RDBMS, cryptography library etc. that you're using before they can design their attacks, that makes their job all the more difficult.
One of the many reasons the MD5 algorithm is not suitable for password hashing is that it is too fast. Algorithms like Bcrypt and Argon2 are made deliberately slow and memory-intensive to prevent brute forcing.
To illustrate this, you are going to code a password cracker.
Find the 4-letter string of lowercase letters (i.e. a-z) which produces the md5 digest eb78e94ff0630770c399893bdb94b454
You can use:
const crypto = require("crypto");
const guess = "frog";
var hash = crypto.createHash("md5").update(guess).digest("hex");
to generate an md5 hash in Node.js
As an extension, make your solution generic (if you haven't already) such that it works given any password length and any character set. Create your own MD5 hashes using different length inputs and character sets and test how long it takes your algorithm to crack them.