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

Protecting sensitive data: classify, minimise, mask

Sort the data an app handles into protection levels, then keep less of it: truncate, mask and tokenise, with a run that checks no masked value leaks.

  • Beginner
  • 20 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 core, Pro depth: 7 of 20 minutes free
Free: Classify first: four levels, each with its own rules · Minimise first, then mask what you must show. With a pass: 3 in-depth parts and 3 interview answers, about 13 minutes.

What you will learn

  • Classify the data an application handles into protection levels with concrete handling rules
  • Limit what is kept and shown by truncating, masking, tokenising or encrypting data
  • Find where sensitive data leaks, from URLs and logs to caches, browser storage and uploads
  • Define retention and deletion for each data class, and map them onto India's DPDP Act duties

Before you start

What you’ll learn · Before you start

What you will learn

  • Classify the data an application handles into protection levels with concrete handling rules
  • Limit what is kept and shown by truncating, masking, tokenising or encrypting data
  • Find where sensitive data leaks, from URLs and logs to caches, browser storage and uploads
  • Define retention and deletion for each data class, and map them onto India's DPDP Act duties

Before you start

On this page

A support screen shows every customer’s full card number, date of birth and home address to 120 agents, and the same API response also feeds the mobile app, an analytics script and the request log. Nothing has been stolen yet, but each customer’s data now sits in four more places than any task needed, and each copy is one more way to lose it. The A04:2025 prevention list starts here: know which data is sensitive, and do not keep what you do not need.

You will sort an application’s data into four protection levels with a rule for each, keep less of it by truncating, masking and tokenising, and check in a recorded run that a masked value never gives the original away.

Classify first: four levels, each with its own rules

Before choosing any control, decide how much each field matters. ASVS 5.0.0 requirement 14.1.1 asks that sensitive data be identified and sorted into protection levels, and 14.1.2 that every level have written rules covering encryption, integrity, retention, logging and access to those logs. Four levels are enough to start, and the rules matter more than the names:

Level Examples Logged Sent to third parties Shown to staff
Public Product names, prices Yes Yes Yes
Internal Order numbers, stock counts Yes Only to processors under contract Yes
Confidential Email, phone, address, date of birth Masked only Only what the task needs Masked, in full on request
Restricted Card numbers, passwords, session tokens, API keys Never Never Never in full

Read the restricted row first: a card number appears in full nowhere a person looks. ASVS 14.1.1 adds a trap that catches many teams: data that is merely encoded is still data in the clear, so a Base64 string or the readable payload of a JWT carries the level of whatever it holds. And a field that nobody has classified is the riskiest of all, because no rule applies to it. Treat it as confidential until someone decides.

Key idea

Give every field one of four levels, and let the level decide where the field may go: into logs, to third parties, onto screens.

Checkpoint

Check: Protection levels and their rules

  1. Question 1 of 2 A session JWT is stored in an analytics event as a Base64 string, and its payload holds a user's email address. What level does the stored string have?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Confidential at least, the level of the email inside it, because encoded data is still data in the clear

    Classify by content: a JWT carrying an email is at least confidential, and a session token is restricted, so it belongs in neither an analytics event nor a log.

  2. Question 2 of 2 A new column, loyalty_tier, appears in the database, and nobody has given it a level. What should the rules do with it until someone decides?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Treat it as confidential, so it is not logged in full or sent to third parties

    Unclassified means unprotected. Give new fields a cautious default, and make classifying a field part of adding it.

Minimise first, then mask what you must show

The cheapest protection is data you do not have. The A04:2025 list says it plainly: data that is not kept cannot be stolen. data minimisation: Collecting, keeping and showing only the personal data a task needs, for only as long as it needs it. Data that is not kept cannot be stolen or leaked. has three moves, collect less, keep less and show less, and four techniques do most of the work:

  • Truncate: keep only part of a value, such as the last four digits of a card, when the rest is never needed again.
  • Mask: keep the value but show it obscured, such as c********@mail.example, with an explicit action to reveal it.
  • Tokenise: replace the value with a random 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. and keep the real one in a separate vault, often a payment provider’s.
  • Keyed hash: store an HMAC of the value with a key held outside the database, when you only need to match equal values: a keyed hash: A hash computed with a secret key, such as HMAC-SHA-256. Equal inputs give equal outputs while the key stays the same, but without the key nobody can rebuild the values by hashing guesses. cannot be rebuilt without the key.

ASVS 14.2.6 asks for the masked form by default: return only the part of a value that a feature needs, and if the whole value is needed, obscure it until the user asks. The program below applies the techniques to 1,000 made-up customers and checks two promises: no masked value contains the original, and a stored token cannot be turned back into the phone number by trying every number with the same first five digits:

Keeping less of 1,000 customers, with two checks Python · mask/keep_less.py
# Four ways to keep less of a value, tried on 1,000 made-up customers (seeded, so every run makes the same ones):
# truncate a card number to its last four digits, mask an email address and a phone number for display, and turn a
# phone number into a keyed token that can still be matched. The checks at the end are the point: no masked value may
# contain the original, and a token must not be reversible by trying every possible number.
import hashlib
import hmac
import random
import secrets

def last_four(card):
    return "**** " + card[-4:]

def mask_email(email):
    name, domain = email.split("@")
    return name[0] + "*" * (len(name) - 1) + "@" + domain

def mask_phone(phone):
    return "*" * (len(phone) - 2) + phone[-2:]

KEY = secrets.token_bytes(32)  # held outside the database, in a secrets manager, in a real app

def keyed_token(phone):
    return hmac.new(KEY, phone.encode(), hashlib.sha256).hexdigest()

def plain_hash(phone):
    return hashlib.sha256(phone.encode()).hexdigest()

rng = random.Random(7)  # test data only: never use the random module for anything secret
people = []
for i in range(1000):
    card = "".join(str(rng.randrange(10)) for _ in range(16))
    phone = "98765" + "".join(str(rng.randrange(10)) for _ in range(5))
    people.append({"card": card, "email": f"customer{i}@mail.example", "phone": phone})

print("customer 0, as stored :", people[0]["card"], people[0]["email"], people[0]["phone"])
print("customer 0, as shown  :", last_four(people[0]["card"]), mask_email(people[0]["email"]), mask_phone(people[0]["phone"]))

leaks = sum(p["card"] in last_four(p["card"]) or p["phone"] in mask_phone(p["phone"]) or p["email"] in mask_email(p["email"]) for p in people)
print("masked values that still contain the original:", leaks, "of", len(people))

same = keyed_token(people[0]["phone"]) == keyed_token(people[0]["phone"])
print("the same phone gives the same keyed token, so duplicates can be found:", same)

# Can the stored form be turned back into the number by trying every number that starts 98765?
target = people[0]["phone"]
weak = plain_hash(target)
tries = next(n for n in range(100_000) if plain_hash(f"98765{n:05d}") == weak) + 1
print(f"plain SHA-256: found the number again after {tries:,} of 100,000 tries")
stored = keyed_token(target)
guessed_key = secrets.token_bytes(32)
hits = sum(hmac.new(guessed_key, f"98765{n:05d}".encode(), hashlib.sha256).hexdigest() == stored for n in range(100_000))
print(f"keyed token, tried without the key: {hits} matches in 100,000 tries")

Output

customer 0, as stored : 5260181590830166 customer0@mail.example 9876513186
customer 0, as shown  : **** 0166 c********@mail.example ********86
masked values that still contain the original: 0 of 1000
the same phone gives the same keyed token, so duplicates can be found: True
plain SHA-256: found the number again after 13,187 of 100,000 tries
keyed token, tried without the key: 0 matches in 100,000 tries

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

None of the 1,000 masked values held its original. The last two lines are the reason to key the hash. A plain SHA-256 of the phone number gave the number back after 13,187 tries, because the program only had to hash the 100,000 numbers that start with 98765 and compare. Without the key, 100,000 tries found nothing. A plain hash of a small set of values, such as phone numbers, PINs or dates of birth, is a table anyone can rebuild, not a protection.

Which technique fits depends on what the feature needs. Answer three questions about a field of yours:

Interactive · answer and compare Which protection fits this field? Answer three questions about one field your application holds. The table rules out what cannot work for it and says why.
Does any feature need the original value back, such as charging a card or calling a phone number?
Do you need to find equal values, such as a phone number registered on two accounts?
Do people see this value on screens, such as support agents or the customer?

Answer the questions above: the table then marks what fits your case and says why the rest does not.

Six ways to hold a sensitive field, against what a feature needs and what a stolen database would reveal
Protection Original back? Match equal values? A stolen database reveals
Do not keep it What is stored: nothing Poor gone for good, which is the point Poor nothing is left to compare Good nothing at all
Truncate What is stored: a part, such as the last four digits Poor the part you cut is gone Mixed only on the kept part, which many values share Good the kept digits only
Mask on screens What is stored: the full value, shown obscured Good stored whole; the mask is only on screens Good the stored value matches exactly Poor the full value: masking protects screens, not the database
Tokenise What is stored: a random token; the value sits in a vault Good only through the vault, with its own access control Mixed only if the vault gives one value the same token every time Good random tokens that mean nothing without the vault
Keyed hash What is stored: an HMAC made with a key kept elsewhere Poor cannot be reversed, by design Good equal values give equal tokens while the key stays the same Good nothing usable: 100,000 tries without the key found nothing
Encrypt the field What is stored: ciphertext; the key is kept elsewhere Good with the key, which lives outside the database Mixed only by decrypting, or with a keyed hash stored beside it Good ciphertext, as long as the key was not stolen with it
How the answers decide
  • “Does any feature need the original value back, such as charging a card or calling a phone number?” No, nothing reads it back: rules out Mask on screens, Tokenise and Encrypt the field. Keeping a value that no feature reads back only adds a copy to lose. Drop it, truncate it, or keep a keyed hash if you must match it.
  • “Does any feature need the original value back, such as charging a card or calling a phone number?” Yes, a feature uses it: rules out Do not keep it, Truncate and Keyed hash. These throw the original away for good, so a feature that needs it back cannot use them.
  • “Do you need to find equal values, such as a phone number registered on two accounts?” Yes, we compare values: rules out Do not keep it and adds a caveat to Truncate, Tokenise and Encrypt the field. Truncated values collide, and comparing tokens or ciphertext needs the vault, the key, or a keyed hash kept beside them.
  • “Do people see this value on screens, such as support agents or the customer?” Yes, people see it: adds a caveat to Tokenise and Encrypt the field. Store it this way and still show it masked by default, with an explicit action to reveal it, as ASVS 14.2.6 asks.
  • “Do people see this value on screens, such as support agents or the customer?” No, only code uses it: rules out Mask on screens. Masking protects what people see. If nobody sees the value, a mask changes nothing about the stored copy.

What you keep can still leave through URLs, logs, caches, browser storage, third parties, exports, backups and file metadata, and each copy needs a retention rule, which India’s DPDP Act makes a legal duty.

Checkpoint

Check: Truncate, mask, tokenise, keyed hash

  1. Question 1 of 2 A team stores SHA-256 hashes of phone numbers instead of the numbers, to find duplicates. Why is this not a protection?

    Choose one answer.

    Show the answer: Question 1 of 2

    Answer: Phone numbers come from a small set, so anyone can hash every candidate and compare, as the run did in 13,187 tries

    Use a keyed hash, an HMAC with a key held outside the database: equal numbers still match, and without the key the run's 100,000 tries found nothing.

  2. Question 2 of 2 Support screens show card numbers as **** 0166, but the database still holds every number in full. What does a stolen copy of that database reveal?

    Choose one answer.

    Show the answer: Question 2 of 2

    Answer: Every full card number, because masking protects only what people see

    Masking limits who sees a value; truncation, tokenisation, a keyed hash or encryption limit what storage holds. For card numbers that no feature needs, keep only the last four.

Where sensitive data leaks, traced with a data map Pro: opens with a Pro, Premium or Ultimate pass

Most leaks are copies nobody planned: a value in a URL, a log line or a cached page. This part follows one value through the places that copy it, then checks twelve flows of a small shop against its data map.

In this part:

  • A value in a URL is copied into at least five places
  • Twelve flows checked against a data map, and what failed
  • Caches, browser storage and logout leave copies behind

1 diagram · 1 runnable example (JavaScript) · about 6 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).

Encrypting what you keep, with a key outside the database Pro: opens with a Pro, Premium or Ultimate pass

Encryption at rest protects a copy of the data only when the key is not in the same copy. This part encrypts one column with envelope encryption, reveals a value through a key service that records it, and changes the key without re-encrypting the data.

In this part:

  • Disk encryption stops a stolen disk, not a breach through the application
  • Envelope encryption: a data key per row, wrapped by a key you never store

1 runnable example (JavaScript) · about 4 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).

Retention and deletion, with India's DPDP Act Pro: opens with a Pro, Premium or Ultimate pass

Data that is kept forever leaks eventually. This part gives every data class a retention rule, runs a sweep with an injected clock, and maps these habits onto the duties of India’s Digital Personal Data Protection Act.

In this part:

  • Every class gets a retention rule, and a sweep enforces it
  • What India’s DPDP Act asks of engineers

1 diagram · 1 runnable example (Python) · about 6 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).

Practise: a profile that shows only what each reader needs

Write the masking functions and a profile response that gives each kind of reader only the fields their task needs. The sample tests run in your browser:

Exercise · Easy · JavaScript

A profile that shows each reader only what they need

A shop's profileFor(user, viewer) in profile.mjs returns the whole customer record to everyone. Write three masks and make the profile return only what each reader needs:

  1. maskCard(card) keeps the last four digits: '4111 1111 1111 1234' becomes '**** 1234'. Ignore spaces and dashes in the input.
  2. maskEmail(email) keeps the first character of the name and the whole domain: 'asha@mail.example' becomes 'a***@mail.example'. A one-character name becomes a single *.
  3. maskPhone(phone) keeps the last two digits and turns every other digit into *, ignoring any spaces or +: '+91 98765 43210' becomes '**********10'.
  4. profileFor(user, viewer) takes a user { name, email, phone, card, birthDate, address, city } and a viewer { role, reveal }:
    • role: 'self': { name, email, phone, address, card }, with the card masked; nobody sees a full card number.
    • role: 'support': { name, email, phone, card }, all masked except the name, plus revealed: []. A field named in viewer.reveal is shown in full and listed in revealed, but only 'email' and 'phone' can be revealed; the card stays masked whatever is asked.
    • role: 'analytics': { city } and nothing else.
    • Any other role gets an Error.

Starter code · profile.mjs

// Starter: every reader gets the whole record. Write the masks and return only what each role needs.

export function maskCard(card) {
  return card;
}

export function maskEmail(email) {
  return email;
}

export function maskPhone(phone) {
  return phone;
}

export function profileFor(user, viewer) {
  return { ...user };
}
The sample tests · profile.test.mjs
import { test, assert } from 'mysmartcopilot:test';
import { maskCard, maskEmail, maskPhone, profileFor } from './profile.mjs';

const asha = {
  name: 'Asha Rao',
  email: 'asha@mail.example',
  phone: '+91 98765 43210',
  card: '4111 1111 1111 1234',
  birthDate: '1994-02-11',
  address: '12 Lake Road, Pune',
  city: 'Pune',
};

test('the masks keep only what is shown', () => {
  assert.equal(maskCard('4111 1111 1111 1234'), '**** 1234');
  assert.equal(maskCard('4111-1111-1111-9876'), '**** 9876', 'dashes are ignored');
  assert.equal(maskEmail('asha@mail.example'), 'a***@mail.example');
  assert.equal(maskEmail('k@mail.example'), '*@mail.example', 'a one-character name');
  assert.equal(maskPhone('+91 98765 43210'), '**********10');
  assert.equal(maskPhone('0123'), '**23');
});

test('the customer sees their own data, but never the full card', () => {
  assert.deepEqual(profileFor(asha, { role: 'self' }), {
    name: 'Asha Rao',
    email: 'asha@mail.example',
    phone: '+91 98765 43210',
    address: '12 Lake Road, Pune',
    card: '**** 1234',
  });
});

test('support sees masked values unless a field is revealed', () => {
  assert.deepEqual(profileFor(asha, { role: 'support' }), { name: 'Asha Rao', email: 'a***@mail.example', phone: '**********10', card: '**** 1234', revealed: [] });
  const shown = profileFor(asha, { role: 'support', reveal: ['phone', 'card', 'birthDate'] });
  assert.equal(shown.phone, '+91 98765 43210', 'phone can be revealed');
  assert.equal(shown.card, '**** 1234', 'the card stays masked');
  assert.equal(shown.birthDate, undefined, 'fields support never sees cannot be revealed');
  assert.deepEqual(shown.revealed, ['phone']);
});

test('analytics gets the city and nothing else', () => {
  assert.deepEqual(profileFor(asha, { role: 'analytics' }), { city: 'Pune' });
});

test('no response contains the full card number, and unknown roles are refused', () => {
  for (const role of ['self', 'support', 'analytics']) {
    const text = JSON.stringify(profileFor(asha, { role, reveal: ['card', 'email', 'phone'] }));
    assert.ok(!text.includes('1111'), `the ${role} response holds no hidden card digits`);
  }
  assert.throws(() => profileFor(asha, { role: 'marketing' }), undefined, 'an unknown role');
  const later = profileFor({ ...asha, taxId: 'TX-77810' }, { role: 'self' });
  assert.equal(later.taxId, undefined, 'a field added later is not sent by default');
});
A hint

Build each response from the list of fields its role may see, never by copying the user and deleting fields: a field added to the user record later must not appear in any response unless someone adds it to a list.

The sample tests run on this device, in your browser (QuickJS): nothing is sent to mysmartcopilot.com. The first run downloads JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

PII Redactor (Mask Personal Data) Paste a log extract or a support ticket and see the personal data in it masked or replaced, before you share it. Office Document Metadata Remover See what an Office file says about its author and save a copy without that metadata.

The self-check below asks about classifying and minimising, starting with whichever of the two your answers show you know less well:

Practice that adapts to you

Practise this lesson

6 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 Put the lesson's four protection levels in order, from the least to the most protected.

    Give each item its position, from 1 (first).

    Show the answer: Question 1 of 6

    Answer:

    1. Public
    2. Internal
    3. Confidential
    4. Restricted

    Public data may go anywhere; internal data stays with the business and its processors; confidential data is masked in logs and screens; restricted data is never shown, logged or shared in full.

  2. Question 2 of 6 Using the lesson's table, which of these belong in the restricted level? Choose every one.

    Choose every answer that is right.

    Show the answer: Question 2 of 6

    Answer:

    • API keys
    • Card numbers
    • Session tokens

    Restricted data is anything that grants access or money on its own. Confidential data identifies a person and is masked; internal data stays with the business.

  3. Question 3 of 6 A new feature stores customers' delivery notes, such as "leave with the guard, call 98xxxx before". Which level do you give the field, and why?

    Choose one answer.

    Show the answer: Question 3 of 6

    Answer: Confidential, because free text written by customers often contains phone numbers, names and addresses

    Classify by the most sensitive thing a field can hold. Free text written by people is confidential by default.

  4. Question 4 of 6 You need to know whether a phone number is already registered, but no feature ever calls the number. Which technique fits best?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: A keyed hash, an HMAC with a key held outside the database

    Match with a keyed hash, keep the original only when a feature needs it, and never rely on an unkeyed hash of a small set of values.

  5. Question 5 of 6 Which test best shows that a masking function is safe to use on every customer record?

    Choose one answer.

    Show the answer: Question 5 of 6

    Answer: Mask many generated records and check that no masked output contains its original value

    Test the property, not one case: no masked value may contain the original, across many generated records.

  6. Question 6 of 6 A shop tokenises card numbers through a payment provider and stores only the tokens. Its database is copied in a breach. What has the attacker got, and what still needs protecting?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: Random tokens that mean nothing on their own, while access to the provider's vault, the account that can use the tokens, still needs protecting

    Tokenisation moves the secret to the vault. The data map then has to protect the vault's credentials, which are restricted.

Interview questions

  1. Fresher to mid · Warm-up

    What is data minimisation, and why does it count as a security control?

    Data minimisation means collecting, keeping and showing only the data a task needs. It counts as a security control because data you do not have cannot be stolen, leaked or misused, which is how the OWASP Top 10 puts it. It has three moves: collect less, keep less and show less. Four techniques do most of the work: truncate a value you never need in full, such as keeping the last four digits of a card; mask what people see, with an explicit action to reveal it; tokenise through a vault, so the database holds random tokens; and keep a keyed hash when you only need to match equal values. Each removes copies of the original, so a breach of any one store reveals less.

  2. Fresher to senior

    A support tool shows each customer's full card number, date of birth and address to 200 agents. Redesign what the screen shows and what the API behind it returns, without making support slower.

    Model answer, Pro. Model answers open with any pass; prices are in the notice on this page. First step and follow-ups

  3. Mid to senior

    Marketing wants the full user profile, email and phone included, sent to a third-party analytics tool so they can build audiences. How do you respond, and what would you send instead?

    Model answer, Pro. First step and follow-ups

  4. Mid to staff

    Design retention for a food-delivery app's order history, saved addresses, support chats and access logs, for a team that deletes nothing today. What rules do you set, and how do you enforce them?

    Model answer, Pro. First step and follow-ups

Flashcards · 5 cards

Recall it, then check

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

  1. What four protection levels does the lesson use, and what do they decide?

    Public, internal, confidential and restricted. The level decides where a field may go: logs, third parties and screens. Restricted data (card numbers, passwords, session tokens, API keys) is never logged, never sent to third parties and never shown in full; confidential data (email, phone, address, date of birth) is logged masked only.

  2. What do ASVS 5.0.0 14.1.1 and 14.1.2 ask for, and what is the trap with encoded data?

    14.1.1: identify sensitive data and sort it into protection levels. 14.1.2: write rules for each level covering encryption, integrity, retention, logging and access to those logs. Data that is merely encoded, such as Base64 or the readable payload of a JWT, is still data in the clear and keeps the level of what it holds. Treat an unclassified field as confidential until someone decides.

  3. What is the cheapest protection for sensitive data?

    Not having it: collect less, keep less and show less, because data that is not kept cannot be stolen (A04:2025). What you do keep can still leave through URLs, logs, caches, browser storage, third parties, exports, backups and file metadata, so each copy needs a retention rule, which India's DPDP Act makes a legal duty.

  4. How do truncating, masking, tokenising and keyed hashing differ?

    Truncate: keep only part of a value, such as the last four card digits. Mask: keep the value but show it obscured, with an explicit action to reveal it (ASVS 14.2.6). Tokenise: replace it with a random token and keep the real value in a separate vault. Keyed hash: store an HMAC with a key held outside the database, when you only need to match equal values.

  5. Why does hashing a phone number with plain SHA-256 not protect it?

    The set of phone numbers is small. The lesson's program only had to hash the 100,000 numbers that start with the same five digits and compare, and found the original after 13,187 tries; against a keyed hash, 100,000 tries found nothing. A plain hash of phone numbers, PINs or dates of birth is a table anyone can rebuild.

Key takeaways

  • Give every field one of four levels with written rules; encoded data, such as a JWT payload, keeps its level.
  • Data you do not keep cannot be stolen: collect less, keep less and show less before protecting anything.
  • Truncate what you never need in full, mask what people see, tokenise through a vault, and key the hash you match on.
  • Test masking as a property: none of 1,000 masked values contained its original.
  • An unkeyed hash of a phone number came back after 13,187 tries, so a hash of a small set of values needs a key.

Terms in this lesson

data minimisation
Collecting, keeping and showing only the personal data a task needs, for only as long as it needs it. Data that is not kept cannot be stolen or leaked.
keyed hash
A hash computed with a secret key, such as HMAC-SHA-256. Equal inputs give equal outputs while the key stays the same, but without the key nobody can rebuild the values by hashing guesses.
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.

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.