Secure memory in payment systems

Kyryll Prytula
7.10.2022

Introduction

Secure memory in payment systems is one of the most important PA:DSS and PCI:DSS topics. While any device involved in payment transaction processing needs to consider memory use, back-end processing and authorisation systems also play a critical role in this story.

Why is secure memory so important?

All payment data that is passed through any payment device needs to be stored in RAM for processing. Once the data is stored in RAM, it can be very easy for an intruder to programmatically scan actual/RAM memory and extract the desired payment data-from card/account numbers, Track 2 and Payment Tokens to clear encryption keys.

Certified QSAs use their internal tools to check for sensitive data leftovers in memory as part of the forensic stage of the PA:DSS audit. Finding such a problem such data leftovers effectively means failing the audit and the latest payments data-exploitation stories confirm the need for this rigour.

As part of EFTlab’s product development, our team evaluated the best techniques for data protection, with the following accepted as our internal standards.

Secure allocators

There are several commonly used strategies to protect secure data in memory; each of them has strengths and weaknesses, so a combination of is recommended.

The first and most obvious one is to protect the system through access restriction. While it is up to system administrators to achieve a well protected DMZ and to implement the latest security patches & recommendations, once past this layer, the attacker’s work is too easy.

Imagine having a naive implementation like this to store the PAN data:

void main() { 
    std::string cardPan = "4567890000000001"; 
};

Compiling and running the snippet above doesn’t mean that the data were wiped out of memory. An inexpensive scan of all system buffers reveals that the memory page remains persists a long time after the application’s variable went out of the scope. A tool like HexDump quickly finds our candidate culprit:

0x00007ffff3d1c110 00 00 00 00 00 00 00 00 00 00 00 00 34 35 36 37 |............4567| 
0x00007ffff3d1c120 38 39 30 30 30 30 30 30 30 30 30 31 00 00 00 00 |890000000001....| 
0x00007ffff3d1c130 00 00 00 00 00 00 40 00 00 00 00 00 00 00 00 00 |......@.........| 
0x00007ffff3d1c140 00 00 00 00 00 00 00 00 00 00 00 00 00 a0 03 00 |................| 
0x00007ffff3d1c150 00 00 00 00 00 10 00 00 00 00 00 00 01 00 00 00 |................| 
0x00007ffff3d1c160 06 00 00 00 00 10 03 00 00 00 00 00 00 90 63 00 |..............c.|

Rather than considering in-memory encryption, EFTlab’s solution is to go the way of output data sanitisation and minimization of the time needed for data exposition in memory through the use of secure allocators. Taking advantage of C++ constructors and destructors, any memory allocation is done in a way that ensures that its memory buffer is wiped out immediately after coming out of the scope. This is achieved through the SecureAllocator template class which uses following allocator and deallocator:

/** 
* Allocate memory for N items using the standard allocator. 
*/ 
pointer allocate(size_type n) { 
// allocate using "global operator new" 
return static_cast(::operator new(n * sizeof(T))); 
} 
/** 
* Release memory which was allocated for N items at pointer P. 
* 
* The memory block is filled with zeroes before being released. 
* The pointer argument is tagged as "volatile" to prevent the 
* compiler optimizing out this critical step. 
*/ 
void deallocate(volatile pointer p, size_type n) { 
std::memset(p, 0, n * sizeof(T)); 
// free using "global operator delete" 
::operator delete(p); 
}

For a more in-depth explanation, check the C++ documentation. A new implementation then looks like this and all memory is zeroed after usage:

void main() { 
SecureAllocator cardPan = "4567890000000001"; 
};

Disclaimer: EFTlab’s SecureAllocator is not released as GPL, but feel free to contact EFTlab Support to get your own copy.

Cryptographic keys protection

In the example above we covered in-memory storage, but there is also a need to secure sensitive data stored on the Host’s persistence device (Database). As in the previous use case, two main strategies can be applied: complete database encryption enforced by a Database engine; and HSM-assisted data encryption. A combination of both is preferable, but using the first brings some worries with it for disaster recovery.

EFTlab’s solution is to use an HSM (Thales/SafeNet) for sensitive data storage. An HSM first generates a number of random Key Encryption Keys (KPK/KEK) and BP-Switch then uses these keys to rotate the Data Protection Keys (DPK/DEK). These are then used to encrypt/decode all sensitive data routed into the system’s persistence, so all the heavy computing labour is handled in the secure zone of the TRMS HSM.

BP-Switch still needs to handle data padding to for the block size needed by the data encryption algorithm in use (8-bytes for DEA). Based on our research and experience, the following options were considered:

  • PKCS5 padding – left justify, pad with binary length of trailer (x41 x42 x43 x05 x05 x05 x05 x05)
  • OneAndZeroes padding – left justify, pad with single x80 and binary zeroes (x41 x42 x43 x80 x00 x00 x00 x00)
  • Zeroes padding – left justify, pad with binary zeroes (x41 x42 x43 x00 x00 x00 x00 x00)
  • Zeroes padding, last gives length – left justify, pad with binary zeroes (x41 x42 x43 x00 x00 x00 x00 x00)

Unfortunately none of the above are secure enough. For example, EBC encryption and the 8byte trailer can still leak information on a larger number of data records. Some of these options are also not applicable for binary data storage, as values of x80 and binary zero x00 are completely acceptable data values. The final problem identified was performance – in all of the options mentioned, the application needs to tackle the last byte(s), where as sequential reading starts from the beginning. This causes unnecessary overhead to the system.

By combining all of the techniques above and adding our own internal requirements, EFTlab’s team devised a new Padding mechanism which adds padded length in front of the data, which is then followed by a number of randomly-generated bytes for padding (x04 x28 x3D x23 xC9 x31 x32 x33). Such an approach allows fast addition and processing of padding, without suffering from the trailing problem. An additional advantage of this approach is that the randomly padded data results in output that is always encrypted differently, even when the data is the same – making it less vulnerable to known types of cryptographic attacks. Following output table demonstrates and explains all applications.

Output examples
Input
(HexDec)
Output
(HexDec)
x31x06 xEB x82 x4E x4D x49 x3D x31
x31 x32x05 x8B xE3 xA3 x7B xB0 x31 x32
x31 x32 x33x04 xD0 xC8 xAF x9D x31 x32 x33
x31 x32 x33 x34x03 x42 x3F x8A x31 x32 x33 x34
x31 x32 x33 x34 x35x02 xD3 xB3 x31 x32 x33 x34 x35
x31 x32 x33 x34 x35 x36x01 x06 x31 x32 x33 x34 x35 x36
x31 x32 x33 x34 x35 x36 x37x00 x31 x32 x33 x34 x35 x36 x37
x31 x32 x33 x34 x35 x36 x37 x38x07 x63 x66 xBA xCB x2A x2F x4F
x31 x32 x33 x34 x35 x36 x37 x38

To support implementation of our product, this functionality has been added to BP-Tools 15.09 release, as detailed in the screens below. We welcome your feedback on this.

Adding padding:

Padding removal:

Share