Skip to main content
  1. Security Insights & Advisories/

Hashing Low Entropy Data & Credit Cards in APAC

Here is an uncomfortable fact: hashing is not the same as protecting. You can store a SHA-256 hash of a credit card number, be fully PCI DSS compliant, and still have effectively no protection at all, because the value you hashed simply does not contain enough entropy to resist brute force.

This trips up otherwise careful engineering teams because hashing feels safe. The hash is one-way, the original cannot be recovered by reversing the function, so surely the data is protected. The flaw is not in the hash. The flaw is in what you fed it.

The entropy problem, in actual numbers #

A 16-digit card number is not random. Its structure is public and fixed:

  • The first 4 to 6 digits are the Issuer Identification Number (IIN): the bank prefix, fully public.
  • The last digit is a checksum, computed by the Luhn algorithm, a formula published in 1954. It is not secret; it is error detection.

Now mask the PAN the way PCI DSS commonly allows: first 4 to 6 digits and last 4 visible, with the middle 6 to 8 hidden:

4532 AAXX XXXX 1234

With only 4 digits known for the IIN, what is left unknown is 8 digits, or at most 100,000,000 possible values. Apply the Luhn checksum and only 1 in 10 of those survive. Your real search space is 10 million values. That is not a password. That is a very small list.

How fast can 10 million hashes be tested? #

This is where it gets worse. SHA-256 is fast by design. It is built for integrity checking at gigabit speed, not for storing secrets. Modern GPU cracking benchmarks are public and reproducible:

HardwareApproximate SHA-256 throughput
1× RTX 4090 GPU~8.5 billion hashes/second
4× RTX 4090 cluster~34 billion hashes/second
8× RTX 4090 cluster~68 billion hashes/second

Ten million guesses divided by 8.5 billion per second is roughly one thousandth of a second. On a single consumer GPU. Even A single GPU computer can use a rainbow table to ‘un-hash’ a credit card number in the blink of an eye.

The conclusion is blunt: Compliant is not secure. On low-entropy fields, even SHA-2 (or SHA-3) is not secure, even when it is compliant. The function is one-way; it is just trivially exhaustible when the input space is tiny. Switching SHA-256 for SHA-512 or SHA-3 does not fix this, because they are equally fast.

What “compliant” really allows #

PCI DSS does not actually tell you to hash PANs with SHA-256. Requirement 3.5 says you must render the PAN unreadable using strong cryptography, which explicitly calls out keyed hashes and encryption, and notes that a hashed and salted index is acceptable where the salt is secret and the hash is not practically reversible. The problem is that a bare, unsalted SHA-256 of a 10-million-value space is, in practice, reversible by exhaustion, so it fails the intent of the requirement even if a checklist tick passes.

Masking (displaying first 4-6 and/or last 4) is a separate control: it protects what an operator sees, not what you store. The two are easily confused, and the confusion is how masked-but-bare-hashed PANs end up in production.

How to protect this kind of data properly #

The fix is to treat low-entropy fields with the same respect you would a password, because mathematically, they are just as weak. The options, in order of preference:

  1. Do not store it at all. Tokenise the PAN and keep the real number in a separate vault or HSM. If you never store the value, there is nothing to brute force.
  2. Keyed hashing (HMAC) with a secret pepper. If you must index by PAN, use an HMAC with a high-entropy key kept outside the database. Without the key, brute force is computationally infeasible regardless of input entropy.
  3. Memory-hard password hashing. Where you need to protect the value with the value alone, use Argon2id (RFC 9106) or scrypt with a per-value random salt and parameters tuned so each guess costs real time and memory. Argon2id at, say, 64 MB memory cost turns that 0.001-second exhaustive search into months of GPU time.
  4. Salts and peppers everywhere. A random salt per value defeats precomputed rainbow tables; a secret pepper defeats offline attacks entirely when it stays secret.

The OWASP Password Storage Cheat Sheet and NIST SP 800-63B both recommend memory-hard functions for low-entropy secrets for exactly this reason.

flowchart TD A[PAN stored] --> B{Needed for indexing?} B -- No --> C[Tokenise / vault / HSM] B -- Yes --> D{Secret key available?} D -- Yes --> E[HMAC with pepper] D -- No --> F[Argon2id / scrypt + salt] style C stroke:#10B981,stroke-width:2px style E stroke:#10B981,stroke-width:2px style F stroke:#10B981,stroke-width:2px

The lesson beyond cards #

This applies to every fixed-format identifier with limited entropy: national ID numbers, phone numbers, dates of birth, and even API keys with poor generation. If the input space is small, the hash function’s speed is your enemy, and “compliant” is not a synonym for “safe.”

Worried about how you are currently protecting PANs or other identifiers? Reach out for a straightforward, sanity check. Contact me on LINE (@PureSecurity) or email (hello@puresecurity.com).

Our API & Application Security Review examines how your code actually stores and transmits sensitive values, and we will tell you plainly where a checklist pass is leaving real data exposed.