记录具有保存敏感数据(待散列)属性的对象
Logging objects with properties which hold sensitive data (to be hashed)
上下文
我正在设计应用程序的日志记录功能。
寻找一个优雅且性能良好的解决方案。
(当然我不会重新实现轮子 :)。我将使用流行的日志记录库——例如 Nlog、Serilog……)。
要求
记录 可能包含具有 敏感信息.
属性的对象
让我们假设一个 class 人:
class Person {
string Name { get; set; }
string SensitiveInfo { get; set; }
}
一个糟糕的(在我看来)方法是做这样的事情:
logger.Debug($"{person.Name}, {Hash(person.SenstiveInfo)}");
为什么我觉得这很糟糕?
- 这是一个调试日志,可能永远不会记录(基于日志记录级别)。因此,它正在浪费 CPU 资源进行散列,而无需任何必要。
- 我宁愿允许对象按原样传递(例如:logger.Debug(person)),而不是强迫开发人员手动分解所有对象属性。
到目前为止的想法...
以下两种方法都需要一个 loggerWrapper,如果可能的话,我想避免:
class LoggerWrapper {
LoggerWrapper (ILogger logger) {
//store the ILogger
}
void Log(object objectToBeLogged, LoggingLevel level) {
//If logging level is not accepted return
_logger.Log(objectToBeLogged, level);
}
}
1.创建一个 ILoggable 接口
interface ILoggable {
string GetLogValue(HashManager hashManager);
}
class Person: ILoggable {
string Name { get; set; }
string SensitiveInfo { get; set; }
string GetLogValue(HashManager hashManager) {
string hashedValue = hashManager.Hash(person.SenstiveInfo);
return $"{person.Name}, {hashedValue}"
}
}
// Now the LogWrapper.Log will expect an ILoggable, and will use the 'GetLogValue' method
// overriding 'ToString()' is an alternative but it is exposed by the 'object' class, and i fear that is not strict enough/can be forgotten.
对这种方法的担忧:对开发人员要求太高?可维护性?
2。注释和反射
class Person {
[Loggable]
string Name { get; set; }
[Loggable, SensitiveInfo]
string SensitiveInfo { get; set; }
}
// Now the logWrapper will rely on reflection (i think) and choose what to log/hash
担心这种方法:性能?
上述方法是否被视为不良做法?
有没有更好的方法?
是否有任何图书馆已经在处理这个问题?
你的第二种方法没问题。
我可以想到几种替代方法,但它们最终会非常接近您所拥有的属性注释。
- 比如在外部文件中保留某种注释信息。
- 或者创建像'SensitiveInfo<T>
'这样的特殊类型,并在必须进行散列的地方使用它,但几乎与注释属性相同。
表演部分:
您可以在应用程序开始时创建一次结构,该结构扫描程序集中的所有类型。该结构可以是将类型名称作为键 ("MyCompany.App.Core.Person"
) 的字典,对于值,您可以使用 Func
object created from dynamically compiled lambda expression,这将 return 在需要时使用散列值记录消息。 (这将是困难的部分)。
重要说明:
jit 编译器在需要之前不会加载 dll,因此尝试在启动开始时创建此结构可以跳过一些程序集。作为替代方法,您可以尝试按需(惰性)填充结构作为记录器包装器的一部分。
然后我将使用日志包装器,它将使用该结构为特定对象创建日志消息。
一些建议:
1. 记录太多信息不一定是坏事 - 所以我不会使用 [Loggable
] 属性 - 只记录对象中的所有内容。这样开发人员的开销就会最小。有时你的对象中可能有很长的字符串,这些字符串并不重要,那么你可以引入 [NonLoggable
] 属性。
2. 您可能会处理层次结构对象 - 这会增加创建 func/labbda 表达式的额外复杂性。
总的来说,这种方法比较复杂,但对开发人员的要求较低。
最后,这取决于您和您的团队愿意做什么以及 spend/saved 的工作量(这将取决于项目的大小......等)。
我认为 第一种方法 将难以维护并增加很多噪音。日志记录应该是基础架构关注点——尽可能自动化。
上下文
我正在设计应用程序的日志记录功能。
寻找一个优雅且性能良好的解决方案。
(当然我不会重新实现轮子 :)。我将使用流行的日志记录库——例如 Nlog、Serilog……)。
要求
记录 可能包含具有 敏感信息.
属性的对象
让我们假设一个 class 人:
class Person {
string Name { get; set; }
string SensitiveInfo { get; set; }
}
一个糟糕的(在我看来)方法是做这样的事情:
logger.Debug($"{person.Name}, {Hash(person.SenstiveInfo)}");
为什么我觉得这很糟糕?
- 这是一个调试日志,可能永远不会记录(基于日志记录级别)。因此,它正在浪费 CPU 资源进行散列,而无需任何必要。
- 我宁愿允许对象按原样传递(例如:logger.Debug(person)),而不是强迫开发人员手动分解所有对象属性。
到目前为止的想法...
以下两种方法都需要一个 loggerWrapper,如果可能的话,我想避免:
class LoggerWrapper {
LoggerWrapper (ILogger logger) {
//store the ILogger
}
void Log(object objectToBeLogged, LoggingLevel level) {
//If logging level is not accepted return
_logger.Log(objectToBeLogged, level);
}
}
1.创建一个 ILoggable 接口
interface ILoggable {
string GetLogValue(HashManager hashManager);
}
class Person: ILoggable {
string Name { get; set; }
string SensitiveInfo { get; set; }
string GetLogValue(HashManager hashManager) {
string hashedValue = hashManager.Hash(person.SenstiveInfo);
return $"{person.Name}, {hashedValue}"
}
}
// Now the LogWrapper.Log will expect an ILoggable, and will use the 'GetLogValue' method
// overriding 'ToString()' is an alternative but it is exposed by the 'object' class, and i fear that is not strict enough/can be forgotten.
对这种方法的担忧:对开发人员要求太高?可维护性?
2。注释和反射
class Person {
[Loggable]
string Name { get; set; }
[Loggable, SensitiveInfo]
string SensitiveInfo { get; set; }
}
// Now the logWrapper will rely on reflection (i think) and choose what to log/hash
担心这种方法:性能?
上述方法是否被视为不良做法?
有没有更好的方法?
是否有任何图书馆已经在处理这个问题?
你的第二种方法没问题。
我可以想到几种替代方法,但它们最终会非常接近您所拥有的属性注释。
- 比如在外部文件中保留某种注释信息。
- 或者创建像'SensitiveInfo<T>
'这样的特殊类型,并在必须进行散列的地方使用它,但几乎与注释属性相同。
表演部分:
您可以在应用程序开始时创建一次结构,该结构扫描程序集中的所有类型。该结构可以是将类型名称作为键 ("MyCompany.App.Core.Person"
) 的字典,对于值,您可以使用 Func
object created from dynamically compiled lambda expression,这将 return 在需要时使用散列值记录消息。 (这将是困难的部分)。
重要说明:
jit 编译器在需要之前不会加载 dll,因此尝试在启动开始时创建此结构可以跳过一些程序集。作为替代方法,您可以尝试按需(惰性)填充结构作为记录器包装器的一部分。
然后我将使用日志包装器,它将使用该结构为特定对象创建日志消息。
一些建议:
1. 记录太多信息不一定是坏事 - 所以我不会使用 [Loggable
] 属性 - 只记录对象中的所有内容。这样开发人员的开销就会最小。有时你的对象中可能有很长的字符串,这些字符串并不重要,那么你可以引入 [NonLoggable
] 属性。
2. 您可能会处理层次结构对象 - 这会增加创建 func/labbda 表达式的额外复杂性。
总的来说,这种方法比较复杂,但对开发人员的要求较低。
最后,这取决于您和您的团队愿意做什么以及 spend/saved 的工作量(这将取决于项目的大小......等)。
我认为 第一种方法 将难以维护并增加很多噪音。日志记录应该是基础架构关注点——尽可能自动化。