Data Masking
NestLens automatically masks sensitive data to protect credentials, tokens, and personal information from being stored in logs.
Overview
Data masking protects:
- Authentication tokens and passwords
- API keys and secrets
- Credit card numbers and personal data
- Session cookies and credentials
- Database connection strings
Automatic Masking
NestLens automatically masks sensitive data across different watchers.
HTTP Request Headers
Sensitive headers are automatically masked:
// Original request
headers: {
'authorization': 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
'x-api-key': 'sk_live_1234567890',
'cookie': 'session=abc123; token=xyz789'
}
// Stored in NestLens
headers: {
'authorization': '********',
'x-api-key': '********',
'cookie': '********'
}
Masked Header Patterns
By default, these headers are masked:
authorizationcookieset-cookiex-api-keyx-auth-token
HTTP Client Requests
Outbound HTTP requests also mask sensitive headers:
// HTTP Client Watcher config
NestLensModule.forRoot({
watchers: {
httpClient: {
sensitiveHeaders: [
'authorization',
'x-api-key',
'api-key',
],
},
},
})
Redis Data Masking
Redis keys containing sensitive patterns are automatically masked.
Auto-Masked Key Patterns
Keys matching these patterns have their values masked:
passwordtokensecretauthkeycredentialsession
Example
// Command: redis.set('user:token:abc', 'secret-value')
// Stored as:
{
command: 'set',
args: ['user:token:abc', '********'],
result: '********'
}
// Command: redis.get('user:name')
// Stored as:
{
command: 'get',
args: ['user:name'],
result: 'John Doe' // Not masked
}
Model Data Masking
Entity data is automatically masked when captured.
Sensitive Field Patterns
These field names are automatically masked:
passwordpasswordHashsecrettokenapiKeyaccessTokenrefreshTokencreditCardssnprivateKey
Example
// User entity
{
id: 123,
email: 'user@example.com',
password: 'hashed_password',
apiKey: 'sk_live_123456',
}
// Stored in NestLens
{
id: 123,
email: 'user@example.com',
password: '********',
apiKey: '********',
}
Disable Data Capture
For maximum security, disable data capture entirely:
NestLensModule.forRoot({
watchers: {
model: {
captureData: false, // Don't capture entity data at all
},
},
})
Custom Masking
Extend masking to protect additional sensitive data.
Custom Sensitive Headers
Add custom headers to mask:
NestLensModule.forRoot({
watchers: {
httpClient: {
sensitiveHeaders: [
'authorization',
'x-api-key',
'x-custom-token', // Add custom header
'x-internal-secret', // Add another
],
},
},
})
Custom Request Parameters
Mask specific request body parameters:
NestLensModule.forRoot({
watchers: {
httpClient: {
sensitiveRequestParams: [
'password',
'credit_card',
'ssn',
'api_secret',
],
},
},
})
Custom Response Parameters
Mask response data:
NestLensModule.forRoot({
watchers: {
httpClient: {
sensitiveResponseParams: [
'access_token',
'refresh_token',
'api_key',
'secret',
],
},
},
})
Entry Filtering for Sensitive Data
Use the filter function to prevent sensitive data from being stored.
Filter by Route
Don't track sensitive endpoints:
NestLensModule.forRoot({
filter: (entry) => {
if (entry.type === 'request') {
// Don't track auth endpoints
if (entry.payload.path.startsWith('/auth/')) {
return false;
}
// Don't track password reset
if (entry.payload.path.includes('/reset-password')) {
return false;
}
}
return true;
},
})
Filter by Content
Remove entries with sensitive data:
NestLensModule.forRoot({
filter: (entry) => {
// Don't store logs containing passwords
if (entry.type === 'log') {
const message = entry.payload.message.toLowerCase();
if (message.includes('password') || message.includes('secret')) {
return false;
}
}
return true;
},
})
Custom Masking in Filter
Mask data before storage:
NestLensModule.forRoot({
filter: (entry) => {
if (entry.type === 'request' && entry.payload.body) {
// Mask sensitive body fields
const body = { ...entry.payload.body };
if (body.password) body.password = '********';
if (body.credit_card) body.credit_card = '********';
entry.payload.body = body;
}
return true;
},
})
Request Body Masking
Control what request body data is captured.
Disable Body Capture
Don't capture request bodies at all:
NestLensModule.forRoot({
watchers: {
request: {
captureBody: false, // Never capture request bodies
},
},
})
Limit Body Size
Prevent large payloads from being stored:
NestLensModule.forRoot({
watchers: {
request: {
maxBodySize: 1024, // 1KB limit
},
},
})
Bodies exceeding this size are truncated:
{
payload: {
body: {
_truncated: true,
_size: 524288 // 512KB actual size
}
}
}
Response Body Masking
Control response data capture.
Disable Response Capture
NestLensModule.forRoot({
watchers: {
request: {
captureResponse: false, // Never capture responses
},
httpClient: {
captureResponseBody: false, // No outbound response bodies
},
},
})
Selective Response Capture
Capture only specific status codes:
NestLensModule.forRoot({
filter: (entry) => {
if (entry.type === 'request') {
// Only capture error responses
if (entry.payload.statusCode < 400) {
delete entry.payload.responseBody;
}
}
return true;
},
})
Production Security
Recommended Production Config
NestLensModule.forRoot({
// Disable in production (safest)
enabled: process.env.NODE_ENV !== 'production',
// OR if enabled in production:
watchers: {
request: {
captureBody: false, // Don't capture request bodies
captureResponse: false, // Don't capture responses
captureSession: false, // Don't capture sessions
},
httpClient: {
captureRequestBody: false, // No outbound request bodies
captureResponseBody: false, // No outbound responses
sensitiveHeaders: [ // Mask all auth headers
'authorization',
'x-api-key',
'cookie',
],
},
model: {
captureData: false, // Never capture entity data
},
},
filter: (entry) => {
// Don't track auth endpoints
if (entry.type === 'request' &&
entry.payload.path.startsWith('/auth/')) {
return false;
}
return true;
},
})
Compliance Considerations
GDPR Compliance
Protect personal data:
NestLensModule.forRoot({
watchers: {
model: {
captureData: false, // Don't store personal data
},
},
filter: (entry) => {
// Mask email addresses
if (entry.type === 'log') {
entry.payload.message = entry.payload.message.replace(
/[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
'***EMAIL***'
);
}
return true;
},
})
PCI DSS Compliance
Never store credit card data:
NestLensModule.forRoot({
filter: (entry) => {
const json = JSON.stringify(entry);
// Detect credit card patterns (simple example)
const cardPattern = /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{4}\b/;
if (cardPattern.test(json)) {
// Don't store entries with potential card numbers
return false;
}
return true;
},
})
HIPAA Compliance
Protect health information:
NestLensModule.forRoot({
watchers: {
request: {
captureBody: false,
captureResponse: false,
},
},
filter: (entry) => {
// Don't track medical endpoints
if (entry.type === 'request') {
const medicalPaths = ['/patient/', '/medical/', '/health/'];
if (medicalPaths.some(path => entry.payload.path.includes(path))) {
return false;
}
}
return true;
},
})
Verification
Audit Captured Data
Regularly review what's being stored:
- Check Dashboard - Review entries for sensitive data
- Export Data - Download entries and inspect
- Database Inspection - Query storage directly
-- SQLite example
SELECT type, payload FROM entries
WHERE payload LIKE '%password%'
OR payload LIKE '%token%'
OR payload LIKE '%secret%';
Test Masking
Verify masking is working:
// Make request with auth
curl -H "Authorization: Bearer secret-token" \
http://localhost:3000/api/users
// Check dashboard
// Authorization header should show: ********
Best Practices
1. Default to Minimal Capture
Start with minimal data capture:
NestLensModule.forRoot({
watchers: {
request: {
captureBody: false,
captureResponse: false,
},
},
})
Enable only what you need for debugging.
2. Use Filtering Liberally
Filter out sensitive routes:
filter: (entry) => {
const sensitivePaths = [
'/auth/',
'/login',
'/password',
'/payment/',
];
if (entry.type === 'request') {
return !sensitivePaths.some(path =>
entry.payload.path.includes(path)
);
}
return true;
}
3. Regular Security Audits
Schedule reviews:
- Monthly data audit
- Quarterly security review
- After any configuration changes
4. Educate Team
Ensure developers know:
- What data is being captured
- How to add custom masking
- When to disable tracking
5. Document Sensitive Patterns
Keep a list of what's considered sensitive in your app:
// sensitive-patterns.ts
export const SENSITIVE_HEADERS = [
'authorization',
'x-api-key',
'x-custom-auth',
];
export const SENSITIVE_PATHS = [
'/auth/',
'/payment/',
'/admin/secrets/',
];
export const SENSITIVE_FIELDS = [
'password',
'creditCard',
'ssn',
];
Next Steps
- Configure Access Control
- Set up IP Whitelisting
- Review Production Usage