日志 redaction(脱敏/屏蔽)
了解如何为敏感数据实现日志 redaction
本指南演示了如何在 OpenTelemetry .NET 日志中实现敏感信息 redaction。 redaction 是保护个人身份信息 (PII)、凭据和其他机密信息等敏感数据的重要实践。
为什么需要 redaction 日志?
Redacting 敏感信息免受日志记录至关重要,原因如下:
- 合规性:满足 GDPR、HIPAA 或 PCI DSS 等监管要求
- 安全性:防止日志文件或日志管理系统中敏感数据的泄露
- 隐私:通过删除个人身份信息 (PII) 来保护用户隐私
- 降低风险:最大程度地减少潜在日志数据泄露的影响
实现基本的 redaction processor
OpenTelemetry 允许您创建自定义处理器,可以在导出日志记录之前对其进行修改。以下是创建基本 redaction processor 的方法:
using System.Collections;
using OpenTelemetry;
using OpenTelemetry.Logs;
internal sealed class MyRedactionProcessor : BaseProcessor<LogRecord>
{
public override void OnEnd(LogRecord logRecord)
{
if (logRecord.Attributes != null)
{
logRecord.Attributes = new MyClassWithRedactionEnumerator(logRecord.Attributes);
}
}
internal sealed class MyClassWithRedactionEnumerator : IReadOnlyList<KeyValuePair<string, object?>>
{
private readonly IReadOnlyList<KeyValuePair<string, object?>> state;
public MyClassWithRedactionEnumerator(IReadOnlyList<KeyValuePair<string, object?>> state)
{
this.state = state;
}
public int Count => this.state.Count;
public KeyValuePair<string, object?> this[int index]
{
get
{
var item = this.state[index];
var entryVal = item.Value?.ToString();
if (entryVal != null && entryVal.Contains("<secret>"))
{
return new KeyValuePair<string, object?>(item.Key, "***REDACTED***");
}
return item;
}
}
public IEnumerator<KeyValuePair<string, object?>> GetEnumerator()
{
for (var i = 0; i < this.Count; i++)
{
yield return this[i];
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
此处理器检查每个日志属性值,并用“REDACTED”替换任何包含字符串“
使用 redaction processor
要使用 redaction processor,请将其添加到您的 OpenTelemetry 日志记录配置中。
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
// Assume MyRedactionProcessor is defined elsewhere
// public class MyRedactionProcessor : BaseProcessor<LogRecord> { ... }
var loggerFactory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(logging =>
{
logging.AddProcessor(new MyRedactionProcessor());
logging.AddConsoleExporter();
});
});
var logger = loggerFactory.CreateLogger<Program>();
// Message will be redacted by MyRedactionProcessor
logger.FoodPriceChanged("", 9.99);
loggerFactory.Dispose();
运行此代码时,输出中任何包含“
高级 redaction 策略
在实际应用中,您会希望通过 SDK 或 OTel Collector processor 实现更复杂的 redaction 策略。
以下是一些使用 SDK 的方法:
1. 使用正则表达式进行模式匹配
public KeyValuePair<string, object?> this[int index]
{
get
{
var item = this.state[index];
var entryVal = item.Value?.ToString();
if (entryVal != null)
{
// Redact credit card numbers
var redactedValue = Regex.Replace(
entryVal,
@"\b(?:\d{4}[-\s]?){3}\d{4}\b",
"***CARD-REDACTED***");
// Redact email addresses
redactedValue = Regex.Replace(
redactedValue,
@"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b",
"***EMAIL-REDACTED***");
if (redactedValue != entryVal)
{
return new KeyValuePair<string, object?>(item.Key, redactedValue);
}
}
return item;
}
}
2. 基于字段的 redaction
您可能希望 redaction 特定字段名,而不考虑其内容。
public KeyValuePair<string, object?> this[int index]
{
get
{
var item = this.state[index];
// Redact sensitive fields by name
var sensitiveFields = new[] { "password", "ssn", "creditcard", "api_key" };
if (sensitiveFields.Any(field => item.Key.Contains(field, StringComparison.OrdinalIgnoreCase)))
{
return new KeyValuePair<string, object?>(item.Key, "***REDACTED***");
}
return item;
}
}
3. 部分 redaction
对于某些数据类型,您可能希望显示部分值。
public KeyValuePair<string, object?> this[int index]
{
get
{
var item = this.state[index];
var entryVal = item.Value?.ToString();
if (item.Key.Equals("email", StringComparison.OrdinalIgnoreCase) && entryVal != null)
{
var parts = entryVal.Split('@');
if (parts.Length == 2)
{
// Show first character of the username and the domain
var redactedEmail = $"{parts[0][0]}***@{parts[1]}";
return new KeyValuePair<string, object?>(item.Key, redactedEmail);
}
}
return item;
}
}
与 ASP.NET Core 集成
对于 ASP.NET Core 应用程序,您可以集成您的 redaction processor。
builder.Services.AddOpenTelemetry()
.WithLogging(logging =>
{
logging.AddProcessor(new MyRedactionProcessor());
logging.AddConsoleExporter();
});