Skip to main content

Non-encrypted confidential information - Credit Cards

Need

Secure encryption and masking of credit card information

Context

  • Usage of Python 3 for building and running Python applications
  • Usage of Django for building web applications

Description

Non compliant code

from django.db import models

class CreditCard(models.Model):
card_number = models.CharField(max_length=16)
card_cvv = models.CharField(max_length=3)
expiry_date = models.CharField(max_length=5)
cardholder_name = models.CharField(max_length=100)

In the above code, we have a Django model CreditCard that stores credit card information. The model includes fields for the card number, CVV, expiry date, and cardholder name.

The vulnerability here lies in the fact that the credit card information is stored as plain text in the database. This means that if an attacker gains access to the database, they would be able to view all the sensitive credit card information stored in it.

This is a direct violation of the PCI DSS (Payment Card Industry Data Security Standard), which stipulates that sensitive cardholder data must be encrypted and adequately protected.

To make matters worse, the CVV (Card Verification Value) is also stored in the database. The PCI DSS explicitly prohibits the storage of CVV post-transaction. So, even if the card number is encrypted, the storage of CVV still remains a significant vulnerability.

In conclusion, the code is vulnerable because it stores sensitive credit card information in plain text and also stores the CVV, both of which are against the PCI DSS requirements.

Steps

  • Use encryption libraries to encrypt sensitive data before storing it in the database.
  • Implement a secure key management system to manage encryption keys.
  • Mask the credit card number and CVV when displaying it. Only the last 4 digits of the card number should be visible.
  • Ensure that the encryption and decryption operations are performed in a secure environment.
  • Use secure communication channels (like HTTPS) to transmit sensitive data.
  • Regularly update and patch the encryption libraries to ensure they are secure against known vulnerabilities.
  • Implement access controls to limit who can view and modify the sensitive data.

Compliant code

from django.db import models
from cryptography.fernet import Fernet
import base64
import os

class CreditCard(models.Model):
card_number = models.BinaryField()
card_cvv = models.BinaryField()
expiry_date = models.CharField(max_length=5)
cardholder_name = models.CharField(max_length=100)

def save(self, *args, **kwargs):
key = base64.urlsafe_b64encode(os.urandom(32))
cipher_suite = Fernet(key)
self.card_number = cipher_suite.encrypt(self.card_number.encode())
self.card_cvv = cipher_suite.encrypt(self.card_cvv.encode())
super().save(*args, **kwargs)

def __str__(self):
key = base64.urlsafe_b64encode(os.urandom(32))
cipher_suite = Fernet(key)
return '{} - {}'.format(self.cardholder_name, cipher_suite.decrypt(self.card_number)[-4:])

The above code fixes the vulnerability by encrypting the credit card number and CVV before storing them in the database. The Fernet class from the cryptography library is used for encryption and decryption. A new encryption key is generated each time a credit card is saved or displayed. The key is generated using the os.urandom function, which generates a string of random bytes suitable for cryptographic use.

The save method is overridden to encrypt the card number and CVV before saving the credit card. The __str__ method is overridden to decrypt the card number when displaying it, and only the last 4 digits are shown.

The card_number and card_cvv fields are changed from CharField to BinaryField to store the encrypted data.

Please note that this is a basic implementation and does not include key management. In a real-world application, you would need to securely store and manage the encryption keys. You would also need to implement access controls to limit who can view and modify the sensitive data.

References