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 Name | Example Values |
---|---|
password | Password , PASSWORD , user_password |
token | Token , 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
Issue | Solution |
---|---|
Sensitive data not masked | Check property name matches masking pattern exactly |
Too much data masked | Review masking patterns for overly broad terms |
Performance impact | Reduce number of masking patterns or object complexity |
Case sensitivity | Masking 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
- User Identification - Associate logs with specific users
- Global Properties - Add consistent context to all logs
- Basic Configuration - Configure masking settings