Skip to main content

Data Masking

Not legal advice

This page describes technical features that can support your compliance work. It is not legal advice, and it does not establish that any system is compliant with any law, regulation or standard. You remain responsible for your own compliance assessment, independent testing and validation, and review by qualified legal and compliance professionals. See the Compliance Disclaimer.

Data masking protects sensitive information (PII/PHI) by replacing identifiable patterns with masked values in logs, error messages, and API responses.

Before You Start​

Overview​

Quick Start​

Configuration​

services.AddDataMasking();

// Or with specific compliance mode
services.AddHipaaDataMasking(); // PHI patterns
services.AddPciDssDataMasking(); // Credit card only
services.AddStrictDataMasking(); // All patterns

Basic Usage​

public class LoggingService
{
private readonly IDataMasker _masker;

public LoggingService(IDataMasker masker)
{
_masker = masker;
}

public void LogMessage(string message)
{
// Mask all sensitive patterns before logging
var safeMessage = _masker.MaskAll(message);
_logger.LogInformation(safeMessage);
}
}

Masking Patterns​

Standard Patterns​

PatternExampleMaskedRule Property
Email[email protected]j***@e***.comMaskEmail
Phone555-123-4567***-***-4567MaskPhone
SSN123-45-6789***-**-6789MaskSsn
Credit Card4111-1111-1111-1111****-****-****-1111MaskCardNumber
IP Address192.168.1.100***.***.***.100MaskIpAddress
Date of Birth12/25/1990**/**/****MaskDateOfBirth

Pattern Details​

Credit card masking preserves the last 4 digits for verification while hiding the PAN:

var input = "Card: 4111-1111-1111-1111";
var masked = _masker.Mask(input, MaskingRules.PciDss);
// Result: "Card: ****-****-****-1111"

SSN masking preserves the last 4 digits per IRS guidelines:

var input = "SSN: 123-45-6789";
var masked = _masker.Mask(input, MaskingRules.Default);
// Result: "SSN: ***-**-6789"

Masking Rules​

Default Rules​

var rules = MaskingRules.Default;
// Enables: Email, Phone, SSN, Credit Card
// Disables: IP Address, Date of Birth

Compliance-Specific Rules​

// PCI-DSS: Credit card numbers only
var pciRules = MaskingRules.PciDss;

// HIPAA: All PHI patterns
var hipaaRules = MaskingRules.Hipaa;
// Enables: Email, Phone, SSN, IP Address, Date of Birth

// Strict: All patterns enabled
var strictRules = MaskingRules.Strict;

Custom Rules​

var customRules = new MaskingRules
{
MaskEmail = true,
MaskPhone = true,
MaskSsn = true,
MaskCardNumber = false, // Disable card masking
MaskIpAddress = true,
MaskDateOfBirth = true,
MaskCharacter = '#' // Use # instead of *
};

services.AddDataMasking(customRules);

Object Masking​

Automatic Property Masking​

Mark properties with attributes for automatic masking. Note that the attributes select properties by name; MaskObject additionally applies the standard pattern set to every string value in the object, so an unannotated property containing a recognised pattern is masked as well.

MaskObject is not available under trimming or Native AOT

MaskObject reflects over the type and round-trips through JSON, so it is annotated [RequiresUnreferencedCode] and [RequiresDynamicCode]. In a trimmed or AOT-published application, mask strings with Mask or MaskAll instead.

public class CustomerDto
{
public string Name { get; set; }

[PersonalData(MaskInLogs = true)]
public string Email { get; set; }

[PersonalData(MaskInLogs = true)]
public string PhoneNumber { get; set; }

[Sensitive(MaskInLogs = true)]
public string SocialSecurityNumber { get; set; }
}

Mask Objects​

var customer = new CustomerDto
{
Name = "John Doe",
Email = "[email protected]",
PhoneNumber = "555-123-4567",
SocialSecurityNumber = "123-45-6789"
};

var masked = _masker.MaskObject(customer);
// Email: j***@e***.com
// PhoneNumber: ***-***-4567
// SocialSecurityNumber: ***-**-6789

Integration Patterns​

Logging Middleware​

public class MaskingLoggingMiddleware : IDispatchMiddleware
{
// Stage has no default on the interface — a middleware that omits it does not compile.
public DispatchMiddlewareStage? Stage => DispatchMiddlewareStage.PreProcessing;

private readonly IDataMasker _masker;

public async ValueTask<IMessageResult> InvokeAsync(
IDispatchMessage message,
IMessageContext context,
DispatchRequestDelegate next,
CancellationToken ct)
{
// Mask message before logging
var safeMessage = _masker.MaskAll(message.ToString() ?? string.Empty);
_logger.LogInformation("Processing: {@Message}", safeMessage);

return await next(message, context, ct);
}
}

Exception Handling​

public class SafeExceptionHandler
{
private readonly IDataMasker _masker;

public string GetSafeErrorMessage(Exception ex)
{
// Mask any PII in exception messages
return _masker.MaskAll(ex.Message);
}
}

API Responses​

public class MaskingActionFilter : IAsyncActionFilter
{
private readonly IDataMasker _masker;

public async Task OnActionExecutionAsync(
ActionExecutingContext context,
ActionExecutionDelegate next)
{
var result = await next();

if (result.Result is ObjectResult objectResult)
{
if (objectResult.Value is CustomerDto dto)
{
// MaskObject round-trips through JSON, so it needs a CONCRETE class.
// Passing a declared `object` returns a JsonElement and loses the typed payload;
// passing an interface or abstract type throws NotSupportedException.
objectResult.Value = _masker.MaskObject(dto);
}
}
}
}

Performance​

The masker uses compiled regex patterns with timeout guards for optimal performance:

MetricValue
OverheadDesign target: under 2% per operation. Not a measured figure — no masking benchmark ships; measure against your own payloads.
Regex engineNonBacktracking — linear time, ReDoS-resistant by construction
Regex timeout2 seconds (a backstop, not the primary defence)
Pattern cachingThe six standard patterns are static and compiled once
Thread safetyFully thread-safe
// The standard patterns are compiled once, in a static initializer.
// Safe for concurrent access from multiple threads.
var masker = new RegexDataMasker(MaskingRules.Default);

MaskObject additionally builds one property-name pattern per masked property on every call, and those are not compiled. Prefer Mask or MaskAll on hot paths.

Security Considerations​

ReDoS Protection​

The standard patterns run on .NET's non-backtracking regex engine, which evaluates in time linear in the input and so has no catastrophic-backtracking case to trigger. A timeout is set as well, as a backstop rather than as the primary defence:

// Internal implementation uses:
new Regex(
pattern,
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.NonBacktracking,
TimeSpan.FromSeconds(2));

Partial Visibility​

Some patterns preserve partial data for verification:

PatternPreservedPurpose
Credit CardLast 4 digitsReceipt verification
SSNLast 4 digitsIRS compliance
PhoneLast 4 digitsCaller verification
IPLast octetNetwork troubleshooting

Attribute-Based Discovery​

Use attributes to mark sensitive properties at the source:

// PersonalData - GDPR Article 4(1) personal data
[PersonalData(Category = PersonalDataCategory.Identity)]
public string Email { get; set; }

// Sensitive - General sensitive data
[Sensitive(Classification = DataClassification.Confidential)]
public string ApiKey { get; set; }

Testing​

Verify Masking​

[Fact]
public void Should_Mask_CreditCard_Preserving_Last4()
{
var masker = new RegexDataMasker(MaskingRules.PciDss);
var input = "Card: 4111-1111-1111-1111";

var result = masker.Mask(input, MaskingRules.PciDss);

result.ShouldBe("Card: ****-****-****-1111");
}

[Fact]
public void Should_Mask_Multiple_Patterns()
{
var masker = new RegexDataMasker(MaskingRules.Strict);
var input = "Email: [email protected], Phone: 555-123-4567";

var result = masker.MaskAll(input);

result.ShouldContain("j***@e***.com");
result.ShouldContain("***-***-4567");
}

Best Practices​

PracticeRecommendation
Default maskingEnable in all environments
Compliance modeMatch your regulatory requirements
Log reviewAudit logs periodically for unmasked data
Attribute usageMark all PII/PHI at definition time
Custom patternsAdd organization-specific patterns as needed

Next Steps​

See Also​