So I have a small electronics project where I am using common Mifare Classic 1K NFC tags as a means of authentication. (Mifare DesiFire is just too expensive)

I know these aren't very secure so here are my ideas on improving on it.

  1. Make Card Read Only
    • This makes it slightly harder to manipulate the card directly but is essentially useless as these cards are very common and easy to copy.
  2. Use the UID of the card
    • Ok idea but still bad, as some cards allow a custom UID. Also a 4 Byte UID is rather small imo. (Some have 7 Bytes thou)
  3. Use the "password" of trailer blocks of the card to block read access.
    • Might work but can be brute forced as the trailer password is 6 Bytes long
  4. Create your own ID on the cards memory and do a random increment every time the card is read if there's a missmatch between the prev id and the current ID you know there are copies around.
    • Pretty much the most secure method I can think of currently. If the card is declined use some other means of identification and get a new card.
  5. Encrypt the card data on the reader instead of using the cards "encryption".
    • Wont stop copying of cards, the content can still be used to authenticate.

Imo. using all the options above together makes it somewhat hard to break these cards' security but not absolutely invulnerable.

Now, my question is, are there any other ways to be able to protect/harden these cards?


1 Answer 1


First and foremost: if you want security, don't use MIFARE cards. They aren't the best at security. Even if a card isn't vulnerable to something like a hardnested attack or a brute-force attack, the card's data can still be sniffed over-the-air (which will include the keys or allow keys and data to be easily recovered).

Generally, most "secure" MIFARE cards will leverage multiple systems to detect the authenticity of the card. Consider a two-phased approach:

First off, leverage the builtin sector keys. The sector keys, while six bytes of size, are surprisingly difficult to crack due to how the cards work. There's no way to "dump a hash" of a MIFARE key block, so you basically need to do the cracking over-the-air/on-card. As you're not limited to ASCII, an attacker is still limited to going through a worst-case of 281.4 trillion possible combinations of bits for a single key.

Second, leverage card state verification. There are a lot of ways to do this, but a common solution is to use the value block system to keep track of how many times the card was used. A sample process exists below.

A two-phased approach like this allows you to keep out casual users who are just trying to clone their cards to their phones without any experience. It also allows you to (somewhat) perform authentication on the readers themselves which is better than nothing. If a card is copied/compromised without the attacker knowing how the system works, a copied card can only be used once before the two cards lose sync with your server and risk one of them causing both to be deactivated.

Solutions like encrypting card data, as you mentioned, won't really do anything to stop an attacker beyond making it harder for them to read the data on the card. MIFARE blocks aren't even readable if the key isn't present, so encryption really becomes an unnecessary second step unless you're trying to protect a secret.

In this example system, you have three components: the reader, a backend authentication server, and a card. On the card, sectors 1 and 2 are used by your application's data:

  • Sector 1 contains a unique serial number assigned to the card. This serial number is protected by a global static key in Key B. The card is set permission mode 101 to only allow ever reads from Key B. Key A does not need to be set.
  • Sector 2 is a bit more complicated, as it contains two blocks. Key B should be randomly generated for each card and Key A can be ignored.
    • Block 0 contains a randomly-generated secret value assigned to the card on creation. Permissions are set on this block as 101 similar to the one used in Sector 1.
    • Block 1 leverages the Value Block feature of MIFARE cards to track the number of times the card has been used. In this case, the Value Block is initialized at value 2,147,483,647 (the maximum for a signed 32-bit number). This block has permissions set to 001 such that Key B can be used to decrement this counter.

Every time a card checks in, the following process happens:

  1. The reader uses the static Key B for Sector 1 to grab the card's unique serial number.
  2. The reader requests card metadata from the authorization server, which responds with:
    • The expected value for the card currently
    • Whether the card can be accepted at this reader (is this user authorized)
    • The (random) Key B for this card
    • The secret value in Sector 2 Block 0 for this card
  3. The reader reads Sector 2 and checks the card's secret value and current counter.
    • If either value is wrong, the card's value block is decremented to below zero, bricking the card.
    • If both values are correct, the card's value block is decremented by one and the card is granted access.
  4. The reader sends the server the card's new expected value. If the card was "bricked", it will be marked as disabled and a security alert is generated.

If additional security is required (for example, the readers can't be trusted), the readers can send the card's values back to the server for authorization. Note that this does add some (generally insignificant) delay, but it can be annoying depending on the application.

Also note that such an example system is highly dependent on everything staying online at all times. In the event that a server is unable to be reached, you may want the system to fail "safe" and grant access in certain cases. If this is to be done, readers should cache read events and then submit them once the server is back online. This would require some restructuring of the card verification system to ensure that all reads are "plausible", and would generally need to take place as a scheduled job or similar concept.

Any such system is (extremely) variable and the actual implementation details depend on how secure and usable you want such a system to be in each of the edge cases. Keep in mind that after a certain point, your return on investment into security starts to decrease and additional complication becomes complication for complication's sake.

  • 3
    Why do you need to check 281.4 trillion possible combinations of bits in order to crack a 6-bit key? Commented Jul 14, 2020 at 11:06
  • @user253751 MIFARE keys are 6 bytes (48 bits) long. 2^(6*8) == 2^48 == 281,474,976,710,656. I updated the answer as well to reflect that correction, my bad.
    – Kaz Wolfe
    Commented Jul 14, 2020 at 20:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .