/

Data Masking

Data masking automatically protects sensitive information in your logs by replacing sensitive values with masked characters. This ensures compliance and security without changing your logging code.

How Data Masking Works

ByteHide Logger automatically scans log messages and context objects for sensitive property names and masks their values:

// Original log
Log.Info("User login", new { Username = "john.doe", Password = "secret123" });

// Output with masking
// [Info] User login { Username = "john.doe", Password = "***" }

Configuration

Configure data masking in LogSettings:

Log.Initialize(new LogSettings
{
    MaskSensitiveData = new[] { "password", "token", "secret", "key" }
    // Default: ["password", "token"]
});

Default Masked Properties

ByteHide Logger masks these properties by default:

Property NameExample Values
passwordPassword, PASSWORD, user_password
tokenToken, ACCESS_TOKEN, auth_token

Custom Masking Patterns

Add Custom Properties

Log.Initialize(new LogSettings
{
    MaskSensitiveData = new[] 
    { 
        // Default patterns
        "password", "token",
        
        // Custom patterns
        "secret", "key", "credential",
        "connectionstring", "api_key", "bearer_token",
        "ssn", "credit_card", "phone", "email"
    }
});

Common Sensitive Properties

var sensitiveProperties = new[]
{
    // Authentication
    "password", "pwd", "pass", "secret", "token", "key",
    "api_key", "apikey", "auth_token", "bearer_token",
    "access_token", "refresh_token", "session_id",
    
    // Database
    "connectionstring", "connection_string", "db_password",
    
    // Personal Information
    "ssn", "social_security", "credit_card", "card_number",
    "phone", "email", "address", "zipcode",
    
    // Application Specific
    "license_key", "encryption_key", "private_key"
};

Log.Initialize(new LogSettings
{
    MaskSensitiveData = sensitiveProperties
});

Masking Examples

Simple Object Masking

Log.Info("User authentication", new 
{ 
    Username = "john.doe",
    Password = "mySecretPassword",  // Will be masked
    Email = "john@example.com"
});

// Output: { Username = "john.doe", Password = "***", Email = "john@example.com" }

Complex Object Masking

var userProfile = new
{
    Id = 123,
    Username = "john.doe",
    Profile = new
    {
        Email = "john@example.com",
        Phone = "555-1234",
        Credentials = new
        {
            Password = "secret123",      // Masked
            ApiKey = "abc123xyz",        // Masked if configured
            LastLogin = DateTime.Now
        }
    }
};

Log.Error("Profile update failed", userProfile, ex);

Database Connection Masking

var dbConfig = new
{
    Server = "localhost",
    Database = "MyApp",
    ConnectionString = "Server=localhost;Database=MyApp;User=admin;Password=secret;", // Masked
    Timeout = 30
};

Log.Error("Database connection failed", dbConfig, ex);

Case-Insensitive Matching

Masking works regardless of casing:

Log.Info("Login attempt", new 
{ 
    PASSWORD = "secret",     // Masked
    Password = "secret",     // Masked
    password = "secret",     // Masked
    user_PASSWORD = "secret" // Masked
});

Nested Object Masking

Masking works at all levels of object hierarchy:

var orderData = new
{
    OrderId = "ORD-123",
    Customer = new
    {
        Name = "John Doe",
        Email = "john@example.com",
        PaymentInfo = new
        {
            CreditCard = "4111-1111-1111-1111",  // Masked if configured
            ExpiryDate = "12/25",
            SecurityCode = "123"                  // Could be masked as "secret"
        }
    },
    Authentication = new
    {
        Token = "bearer_abc123xyz",              // Masked
        ApiKey = "key_xyz789"                    // Masked if configured
    }
};

Log.Error("Order processing failed", orderData, ex);

String Message Masking

Masking also works in string messages when sensitive data appears:

// This will be masked if the message contains sensitive property patterns
Log.Info("User password updated successfully");
Log.Error($"Failed to authenticate with token: {userToken}"); // Token value masked

Configuration Examples

Development Environment

// Minimal masking for development
Log.Initialize(new LogSettings
{
    MaskSensitiveData = new[] { "password", "token" }
});

Production Environment

// Comprehensive masking for production
Log.Initialize(new LogSettings
{
    MaskSensitiveData = new[] 
    { 
        // Authentication
        "password", "pwd", "pass", "token", "key", "secret",
        "api_key", "auth_token", "bearer_token", "access_token",
        
        // Database
        "connectionstring", "connection_string",
        
        // Personal data
        "ssn", "credit_card", "phone", "email",
        
        // Application specific
        "license_key", "encryption_key"
    }
});

Compliance-Focused Configuration

// GDPR/HIPAA compliant masking
Log.Initialize(new LogSettings
{
    MaskSensitiveData = new[] 
    { 
        // PII (Personally Identifiable Information)
        "email", "phone", "address", "ssn", "social_security",
        "first_name", "last_name", "full_name", "date_of_birth",
        
        // Financial
        "credit_card", "card_number", "account_number", "routing_number",
        
        // Medical (HIPAA)
        "patient_id", "medical_record", "diagnosis",
        
        // Authentication
        "password", "token", "key", "secret"
    }
});

Performance Considerations

Masking Overhead

Data masking adds minimal performance overhead:

// ✅ Efficient - Masking is fast for simple objects
Log.Error("Login failed", new { Username = "john", Password = "secret" }, ex);

// ⚠️ Consider - Large objects may have slight overhead
Log.Error("Complex operation failed", veryLargeObjectWithManyProperties, ex);

Optimization successs

// ✅ Good - Only include necessary properties in context
Log.Error("Payment failed", new 
{ 
    TransactionId = payment.Id,
    Amount = payment.Amount,
    // Don't include entire payment object
}, ex);

// ✅ Good - Use specific masking patterns
MaskSensitiveData = new[] { "password", "api_key" } // Only what you need

// ❌ Avoid - Over-broad masking patterns
MaskSensitiveData = new[] { "data", "value", "info" } // Too generic

Testing Data Masking

Verify Masking Works

public void TestDataMasking()
{
    Log.Initialize(new LogSettings
    {
        ConsoleEnabled = true,
        MaskSensitiveData = new[] { "password", "secret" }
    });
    
    // Test basic masking
    Log.Info("Test log", new { Username = "test", Password = "should-be-masked" });
    
    // Check console output to verify masking
}

Unit Testing with Masking

[Test]
public void Should_Mask_Sensitive_Data_In_Logs()
{
    // Arrange
    var testData = new { Username = "test", Password = "secret123" };
    
    // Act
    Log.Error("Test error", testData, new Exception("Test"));
    
    // Assert - Check that sensitive data is masked in log output
    // (Implementation depends on your testing framework)
}

Best Practices

Data Masking Best Practices

  • Start with defaults: Begin with ["password", "token"] and add as needed
  • Review regularly: Audit your masking patterns periodically
  • Test thoroughly: Verify masking works in all environments
  • Document patterns: Keep a list of your organization's sensitive property names
  • Consider compliance: Include patterns required by GDPR, HIPAA, PCI-DSS

Troubleshooting

Common Issues

IssueSolution
Sensitive data not maskedCheck property name matches masking pattern exactly
Too much data maskedReview masking patterns for overly broad terms
Performance impactReduce number of masking patterns or object complexity
Case sensitivityMasking is case-insensitive by default

Debugging Masking

// Test specific masking patterns
var testObject = new 
{ 
    NormalField = "visible",
    Password = "should-be-masked",
    CustomSecret = "should-be-masked-if-configured"
};

Log.Info("Masking test", testObject);
// Check output to verify which fields are masked

Next Steps

Previous
Basic Logging