Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Web Security (OWASP Top 10:2025)  Module 5 – A04:2025 Cryptographic Failures

Spotting cryptographic failures in web code

What to look for when code uses cryptography: weak randomness, encryption that misses changes, loose MAC and signature checks, and data kept without need.

  • Intermediate
  • 30 minutes
  • Examples run with Node.js 24.21.0, quickjs 0.32.0, Python 3.14.8, Pyodide 314.0.7 and sql.js 1.14.2
  • By MySmartCoPilot
Free intro, Pro: 5 of 30 minutes free
Free: Five questions to ask of every line that uses cryptography. With a pass: the other 8 sections and 3 interview answers, about 25 minutes.

What you will learn

  • Recognise weak algorithms, modes and key handling in a code review
  • Use secure randomness for tokens and identifiers
  • Use authenticated encryption through a high-level library
  • Verify signatures and MACs with a pinned algorithm and a constant-time comparison
  • Decide what data to encrypt, hash, tokenise or not store at all

Before you start

What you’ll learn · Before you start

What you will learn

  • Recognise weak algorithms, modes and key handling in a code review
  • Use secure randomness for tokens and identifiers
  • Use authenticated encryption through a high-level library
  • Verify signatures and MACs with a pinned algorithm and a constant-time comparison
  • Decide what data to encrypt, hash, tokenise or not store at all

Before you start

On this page

A cryptographic failure rarely looks like a broken cipher. It looks like working code: Math.random() making a reset token, MD5 in a password check, a fixed value for the Initialisation vector: a value that a cipher mode uses together with the key, so that encrypting the same data twice gives different results. It need not be secret, and it must be new for each message., an == between two message authentication code (MAC): A short tag computed from a message and a secret key. Only a holder of the key can make a tag that matches, so a match shows that the message is unchanged and came from a key holder. HMAC-SHA-256 is a common MAC.. OWASP’s A04:2025 collects 32 weaknesses of this kind, and a reviewer finds them by looking at five places: where random values come from, how data is encrypted, how a value is checked, how long keys and tokens live, and whether the data should exist at all.

This lesson gives you those five questions, the safe building block that answers each, and a reading of the lines that look fine and are not. The first section names the five places and runs one failure that passes every test.

Five questions to ask of every line that uses cryptography

A token in a reset link, an encrypted column, a signed cookie and a stored password all come down to the same questions. Ask them in this order of every pull request that touches one of these, before you read a line of the algorithm:

Interactive · stage by stage Five questions for every use of cryptography Step through the questions a reviewer asks of a line that makes, hides, checks or keeps a secret value.
Five questions for every use of cryptography, step 5 of 5. Showing Where does it come from?, How is it hidden?, How is it checked?, How long does it live?, Should it exist at all?. In focus: Should it exist at all?.Where does it come from?unpredictable bytesHow is it hidden?authenticated encryptionHow is it checked?a MAC or a signatureHow long does it live?expiry and rotationShould it exist at all?keep less data

The drawing shows the whole scene; its 5 steps are listed below.

Every step as text
  1. Where does it come from?. A value that must not be guessed, such as a token, a key or a nonce, has to come from the operating system's source of unpredictable bytes. A generator built for simulations is a different tool.
  2. How is it hidden?. Encryption hides data. Authenticated encryption also notices when the data was changed, and a reviewer wants both properties from any code that encrypts.
  3. How is it checked?. A MAC or a signature shows that a value was made by someone with the right key. The check has to cover the whole value, use an algorithm that you chose, and give nothing away while it runs.
  4. How long does it live?. Tokens should expire, keys should be replaceable, and neither belongs in source code. A value with no end date is a value that stays dangerous after it leaks.
  5. Should it exist at all?. The safest value is one that was never stored. Before any algorithm is chosen, ask whether the data is needed, or whether a stand-in would do.

Each question has a building block that answers it:

  • Randomness. A CSPRNG: A cryptographically secure pseudo-random number generator: a source of random bytes, fed by the operating system, from which nobody can predict the next output. Tokens, keys and nonces must come from one., a cryptographically secure pseudo-random number generator, is the operating system’s source of unpredictable bytes, and tokens, keys and cipher nonce: A number used once with an encryption key. A cipher mode takes a fresh one with each message, so that equal messages do not give equal results. It need not be secret, and it must not repeat under the same key. must come from it.
  • Encryption. authenticated encryption: Encryption that also produces an authentication tag, so that decryption refuses data that was changed after it was encrypted. AES-GCM is the usual example., such as AES-GCM used through a high-level library, hides data and also fails loudly when any part of it was changed.
  • Checking. A MAC is a keyed fingerprint that only holders of the key can make. A signature does the same with a private key, and a constant-time comparison: A comparison whose running time does not depend on how much of the two values matches, so a timer learns nothing about where they first differ. It is the right way to compare MACs, signatures and secret tokens. checks either one without giving anything away.
  • Lifetime. Tokens expire and keys are rotated, so a value that leaks stops being useful.
  • Data. tokenisation: Replacing a sensitive value, such as a card number, with a random token and keeping the real value in a separate vault with its own access control. The token reveals nothing on its own. More in “Protecting sensitive data: classify, minimise, mask” replaces a value such as a card number with a random stand-in that a provider keeps, and the safest data is data that was never stored. For a new column, this is the question to ask first.

One failure passes every test: a reset token made with Python’s random module, which is built for simulations, and seeded with the clock. The program prints what such a token is worth next to one from secrets, which draws on the operating system’s source:

A clock-seeded token and a secrets token, side by side Python · guess/seeded_token.py
# One failure that passes every test: a reset token made with Python's random module, which is built for simulations,
# and seeded with the clock. The token looks like 16 random characters. Anyone who knows roughly when it was made has
# at most one day of seconds to try. secrets draws from the operating system's source of unpredictable bytes instead.
import math
import random
import secrets

ALPHABET = "abcdefghijklmnopqrstuvwxyz0123456789"


def weak_token(seed):
    rng = random.Random(seed)
    return "".join(rng.choice(ALPHABET) for _ in range(16))


looks = 16 * math.log2(len(ALPHABET))
print(f"a 16-character token over {len(ALPHABET)} symbols looks like {looks:.1f} bits")
print("the same seed gives the same token:", weak_token(1_760_000_000) == weak_token(1_760_000_000))
print("the next second gives a different one:", weak_token(1_760_000_000) != weak_token(1_760_000_001))
seconds_in_a_day = 24 * 60 * 60
print(f"seeded with the second it was made, one day holds {seconds_in_a_day:,} tokens: {math.log2(seconds_in_a_day):.1f} bits")
token = secrets.token_urlsafe(32)
print(f"secrets.token_urlsafe(32): {len(token)} characters from 32 random bytes, 256 bits")
print("two of them are equal:", secrets.token_urlsafe(32) == secrets.token_urlsafe(32))

Output

a 16-character token over 36 symbols looks like 82.7 bits
the same seed gives the same token: True
the next second gives a different one: True
seeded with the second it was made, one day holds 86,400 tokens: 16.4 bits
secrets.token_urlsafe(32): 43 characters from 32 random bytes, 256 bits
two of them are equal: False

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 seeded_token.py

The weak token looks like 82.7 bits of secret. Seeded with the second it was made, it has 86,400 possible values a day, 16.4 bits, and the same seed gives the same token every time. The one from secrets has 256 bits, and two draws from it differ. No test that asks whether a token looks random can tell the two apart, so the review has to happen in the code.

Checkpoint

Check: The five places a review looks

  1. Question 1 of 2 The reset token in the first program is 16 characters long and looks like 82.7 bits. Why is it a weak token?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: It comes from a generator seeded with the clock, so an attacker has only 86,400 candidates a day

    The text looks like 82.7 bits, but a guess only has to cover the seconds in a day: 86,400 values, 16.4 bits. Where a value comes from decides what it is worth, and secrets draws on the operating system.

  2. Question 2 of 2 A pull request adds a signed download link. The signature is a MAC of the file name, compared with ==, and the link never expires. Which of the five questions does it fail? Choose every one.

    Choose every answer that is right.

    Show the answer: Question 2 of 2

    Answer:

    • How long does it live?
    • How is it checked?

    Two of the five questions fail: the check and the lifetime. A reviewer asks all five of every use, and the answers here are a constant-time comparison and an expiry time that is part of what is signed.

Flashcards · 4 cards

Recall it, then check

Say the answer to yourself before you look: recalling it is what makes it stick.

  1. What five places does a reviewer check in every line that uses cryptography?

    Where random values come from; how data is encrypted; how a value is checked; the lifetime of keys and tokens; and whether you need the data at all. A04:2025 collects 32 weaknesses of this kind, and they look like working code: Math.random() making a reset token, MD5 in a password check, a fixed IV, an == between two MACs.

  2. Which safe building block answers each of the five questions?

    Randomness: a CSPRNG, which draws unpredictable bytes from the operating system. Encryption: authenticated encryption, such as AES-GCM used through a high-level library, which hides data and also fails loudly when any part was changed. Checking: a MAC or signature compared in constant time. Lifetime: expiring tokens and rotated keys. Data: tokenisation, and above all not storing the data.

  3. Why is a reset token made with Python's random module, seeded with the clock, weak?

    random is built for simulations. Seeded with the second it was made, such a token has only 86,400 possible values a day, about 16.4 bits, although it looks like 82.7 bits, and the same seed gives the same token every time. A token from secrets draws on the operating system and has 256 bits. No test that asks whether a token looks random can tell them apart.

  4. Which of the five questions do you ask first about a new column of sensitive data?

    Whether the data should exist at all. Tokenisation replaces a value such as a card number with a random stand-in that a provider keeps, and the safest data is data that was never stored.

Twelve lines, ten smells: what a scanner flags and what only a reviewer finds Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • Randomness: how many guesses a token costs, by how it was made
  • Encryption that notices change, and encryption that does not
  • Verifying: whole MACs, constant-time comparison and an algorithm you chose
  • Choosing the primitive by the job, and keeping less to protect
  • Practise: six pieces of security code to fix
    • Fix six small pieces of security code
  • Interview questions
    • A teammate built passwordless sign-in. The emailed link carries a token made with Math.random().toString(36).slice(2). The token is stored as it is, never expires, and works any number of times. Review it: what is wrong, and what do you change?
    • Compliance asks you to encrypt the phone-number column of a table of 20 million users. The app also looks users up by phone number, and one report needs the last four digits. How do you design it, and where does the key live?
    • A partner's webhooks carry a signature header: eight hex characters of an MD5 over the body and a shared key. Our handler compares it with ==, after parsing the body, and the key is three years old. The partner cannot change quickly. What do you ask for, and what do you do meanwhile?
  • Key takeaways

5 runnable examples (JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 47 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Terms in this lesson

authenticated encryption
Encryption that also produces an authentication tag, so that decryption refuses data that was changed after it was encrypted. AES-GCM is the usual example.
cipher nonce
A number used once with an encryption key. A cipher mode takes a fresh one with each message, so that equal messages do not give equal results. It need not be secret, and it must not repeat under the same key.
constant-time comparison
A comparison whose running time does not depend on how much of the two values matches, so a timer learns nothing about where they first differ. It is the right way to compare MACs, signatures and secret tokens.
CSPRNG
A cryptographically secure pseudo-random number generator: a source of random bytes, fed by the operating system, from which nobody can predict the next output. Tokens, keys and nonces must come from one.
message authentication code (MAC)
A short tag computed from a message and a secret key. Only a holder of the key can make a tag that matches, so a match shows that the message is unchanged and came from a key holder. HMAC-SHA-256 is a common MAC.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.