How to Mask sensitive data after record creation

In modern applications, especially those handling personal, financial, or confidential information, data security and privacy are critical. Sensitive data such as Aadhaar numbers, PAN cards, credit card details, phone numbers, email addresses, or medical records must be protected from unauthorized access. One common and effective technique is data masking, where sensitive fields are partially or fully hidden after a record is created.
Masking data after record creation ensures that:
- Sensitive information is stored securely
- Users only see what they are authorized to see
- Compliance requirements (GDPR, HIPAA, PCI-DSS, etc.) are met
- Risks of data leakage are minimized
This article explains how to mask sensitive data after record creation, why it is important, different masking strategies, and provides practical code examples using triggers, backend logic, and best practices.
What Is Data Masking?
Data masking is the process of obscuring sensitive information by replacing a portion of the data with symbols, characters, or random values. The goal is to make the data unreadable or partially readable without exposing the original value.
Examples of Data Masking
- PAN Number:
ABCDE1234F→*****1234F - Credit Card:
4111 1111 1111 1111→XXXX-XXXX-XXXX-1111 - Phone Number:
9876543210→******3210 - Email:
user@example.com→u***@example.com
Masking differs from encryption because masked data is not meant to be restored, while encrypted data can be decrypted.
Why Mask Data After Record Creation?
Masking data after record creation is useful when:
- The original value is needed temporarily (for validation or processing)
- The system must store a masked version for regular users
- Only privileged users or systems should access full values
- Regulatory rules require minimal exposure of sensitive data
For example, during form submission, the system may accept a full PAN or credit card number. Once validated and stored securely, the displayed value is masked to protect user privacy.
Common Scenarios for Data Masking
- Customer onboarding forms
- Payment and billing systems
- Healthcare and insurance records
- CRM systems storing personal details
- Government and financial applications
Masking Approaches
1. Frontend Masking
- Masking applied at UI level
- Original data still exists in backend
- Useful for display-only protection
2. Backend Masking (Recommended)
- Data is masked and stored in database
- Prevents exposure even at data level
- More secure and compliance-friendly
This article focuses on backend masking after record creation.
Masking Data Using a Trigger (Salesforce Apex Example)
In platforms like Salesforce, masking is often implemented using after insert triggers, ensuring that data is masked immediately after record creation.
Example Use Case
Mask a PAN number field after a record is created.
PAN Masking Logic
- Keep last 4 characters visible
- Replace remaining characters with
*
Trigger Code Example
trigger MaskSensitiveDataTrigger on Customer__c (after insert) {
List<Customer__c> recordsToUpdate = new List<Customer__c>();
for (Customer__c cust : Trigger.new) {
if (cust.PAN_Number__c != null && cust.PAN_Number__c.length() > 4) {
Integer visibleChars = 4;
Integer maskLength = cust.PAN_Number__c.length() - visibleChars;
String maskedValue = String.valueOf('*').repeat(maskLength) +
cust.PAN_Number__c.substring(maskLength);
Customer__c updatedCust = new Customer__c(
Id = cust.Id,
PAN_Number__c = maskedValue
);
recordsToUpdate.add(updatedCust);
}
}
if (!recordsToUpdate.isEmpty()) {
update recordsToUpdate;
}JavaScriptThis trigger runs after insert, ensuring the record is created first and then updated with masked data.
Using a Helper Class for Reusability
Best practice is to move logic into a helper class.
Helper Class
public class DataMaskingUtil {
public static String maskValue(String input, Integer visibleChars) {
if (String.isBlank(input) || input.length() <= visibleChars) {
return input;
}
Integer maskLength = input.length() - visibleChars;
return String.valueOf('*').repeat(maskLength) + input.substring(maskLength);
}
}JavaScriptUpdated Trigger
trigger MaskSensitiveDataTrigger on Customer__c (after insert) {
List<Customer__c> updates = new List<Customer__c>();
for (Customer__c cust : Trigger.new) {
if (cust.PAN_Number__c != null) {
updates.add(new Customer__c(
Id = cust.Id,
PAN_Number__c = DataMaskingUtil.maskValue(cust.PAN_Number__c, 4)
));
}
}
if (!updates.isEmpty()) {
update updates;
}
}JavaScriptThis approach improves maintainability and testability.
Masking Multiple Fields
You can mask multiple sensitive fields in a single trigger.
Example Fields
- PAN
- Phone Number
trigger MaskMultipleFieldsTrigger on Customer__c (after insert) {
List<Customer__c> updates = new List<Customer__c>();
for (Customer__c c : Trigger.new) {
Customer__c rec = new Customer__c(Id = c.Id);
rec.PAN_Number__c = DataMaskingUtil.maskValue(c.PAN_Number__c, 4);
rec.Phone__c = DataMaskingUtil.maskValue(c.Phone__c, 4);
rec.Email__c = c.Email__c != null ? c.Email__c.substring(0,1) + '***@' + c.Email__c.split('@')[1] : null;
updates.add(rec);
}
update updates;
}JavaScriptMasking Using Platform Events or Async Jobs
For high-volume systems, masking can be moved to asynchronous processing.
Queueable Apex Example
public class MaskDataQueueable implements Queueable {
List<Id> recordIds;
public MaskDataQueueable(List<Id> ids) {
this.recordIds = ids;
}
public void execute(QueueableContext context) {
List<Customer__c> customers = [SELECT Id, PAN_Number__c FROM Customer__c WHERE Id IN :recordIds];
for (Customer__c c : customers) {
c.PAN_Number__c = DataMaskingUtil.maskValue(c.PAN_Number__c, 4);
}
update customers;
}
}
This approach avoids performance issues in bulk operations.JavaScriptHandling Permissions and Security
Masking should complement, not replace, security controls:
- Use Field-Level Security (FLS)
- Apply profiles and permission sets
- Restrict access to original data
- Log access attempts to sensitive fields
For admin users, consider storing:
- Original value in an encrypted field
- Masked value in a display field
Best Practices for Data Masking
- Always bulkify triggers
- Avoid hardcoding mask patterns
- Use utility classes
- Mask data as early as possible
- Combine masking with encryption
- Follow compliance regulations
- Test thoroughly with edge cases
Testing the Masking Logic
Test Class Example
@isTest
public class DataMaskingUtilTest {
@isTest
static void testMaskValue() {
String result = DataMaskingUtil.maskValue('ABCDE1234F', 4);
System.assertEquals('*****1234F', result);
}
}JavaScriptTesting ensures your masking logic works correctly and remains reliable.
Conclusion
Masking sensitive data after record creation is a critical security practice in modern applications. By implementing backend masking using triggers, helper classes, or asynchronous jobs, organizations can protect user privacy while maintaining system functionality.
A well-designed masking strategy ensures compliance, improves trust, and reduces the risk of data breaches. With proper planning, reusable code, and best practices, masking can be seamlessly integrated into any enterprise system.
Related Posts

How to Automatically create a follow-up Task when a Lead is converted

How You need to update a related child record whenever a parent record’s status changes, but only if the status is “Closed Won.” How would you design this in Apex?
