Amazing Arch极客思维运维驿站

在日志记录中保护用户隐私数据的七个最佳实践

2019-03-20  本文已影响4人  极客人

2019年度“315”晚会人工智能拨打骚扰电话的情节,让大众了解到在信息时代,保护个人隐私的重要性。本篇文章分享了在日志记录中保护用户隐私数据的七个最佳实践。

与“中国人愿意用隐私交换便利性”的心态完全不同,欧美国家在个人隐私保护方面明显走得更早也更远一些。在2018年5月GDPR发布前后的一段时间里,保护个人隐私相关的需求被迅速提高了优先级,而像我这样一个开发国际化产品的普通程序员,日常工作也因此受到影响,我们放下手中的业务需求卡(Story),转而去做GDPR相关的安全需求。

从公司最高层面,自上而下,我们采取了一系列相关动作,比如梳理我们基础设施架构图、数据流图、数据格式……,而其中很大一部分就是保护日志中的个人信息。

一般在医疗保健或金融行业中,限制访问客户的敏感数据有着非常严格的规定,尤其欧洲GDPR颁布之后,公司泄露个人数据的后果也非常严重。如果没有适当的流程、工具或者意识,为了开发调试的方便,常常会无意地将隐私数据写入日志文件。

并没有一种一劳永逸的方式来避免个人信息出现在日志中,但我们可以通过下面的措施,并内建在自己平时的开发工作中,来尽量规避出现个人隐私安全问题。

一、确定什么是隐私数据

在我们深入讨论怎样避免个人隐私数据出现在日志之前,我们来界定什么是隐私数据

个人隐私信息不一而足,重要的是要根据实际情况彻查应用内的数据,来确定什么是敏感的.

二、分离隐私数据

处理隐私数据时,应尽量减少系统使用这些数据。例如,在数据库表设计时,使用SSN或电子邮件地址作为人的主键的确很顺手,但是,如果这样做,系统在许多不同部分(数据库表,API端点等)都要处理和存储这些敏感字段,更好的方法是分离隐私数据,只在在必要时才使用它。

一种常见的解决方案是使用查找表用随机ID替换隐私字段。例如:

SSN | 外键
------------------------- 
999-99-9999 |5a2_cXKrt32DcWOJpJlyhr7FhTcLPfvlEAb1eA2H

即使SSN是主键,主Person表外的表和服务也只使用外键。

三、避免在URL中出现个人隐私信息

如果您正在构建RESTful API并且您的用户数据是在电子邮件地址上键入的,则可能很容易拥有一个端点,如:/user/<email>。请求URL通常由代理和Web服务器记录,因此如果您这样做,电子邮件必然会以日志结束。要将敏感数据保留在您的网址之外,您可以选择几个选项。

选项1.根据建议#1,不要将敏感字段用作唯一标识符。对于端点URL,请改用这些外部ID。

选项2.违反REST原则并将敏感值作为POST正文的一部分传递,即使它是只读请求。Web服务器通常不会记录POST请求的正文,因此您的敏感字段不会记录日志。

问题

在设计早期,您应确定系统中哪些数据被视为敏感数据。如果你还没有这样做,那么可能需要付出艰苦的努力才能在游戏后期进行类似的API设计更改。

四、对象打印重写toString方法

因此,您已经划分了代码(#1)并将数据保留在URL(#2)之外。但是,您的用户端点包含一些日志记录语句,以帮助调试服务。它可能看起来像:

logger.info("为用户$ {user}更新电子邮件);

代码库中的某个位置是将a序列化为user字符串的方法。也许它在课堂定义中。在该定义中,请确保使用敏感数据编辑字段:

class UserAccount { 
  id:string 
  username:string 
  passwordHash:string 
  firstName:string 
  lastName:string 

  ... 

  public toString(){ 
    return "UserAccount (${this.id})"; 
}

记录所有字段可能很诱人toString,但事实证明你真的只需要id。如果在调试过程中需要跟踪有关用户的更多详细信息,则可以在完成后查找它们id

问题

这不会阻止开发人员直接记录字段,例如: logger.info("The user's details are: ${user.firstName} ${user.lastName}");

五、结构化对象打印时屏蔽隐私字段

通过基于字符串的API记录,console.log()或者printf两者都需要将数据转换为字符串,现在通常被认为是一种反模式。将数据吐出来进行调试可能很容易,但解析这些数据很痛苦,而且可能缺少有用的上下文。使用结构化日志记录而不是字符串,可以记录键/值或嵌套对象。有关当前上下文的某些详细信息(例如,请求ID或服务器主机名)可以自动注入请求中。

如果你不熟悉结构化日志,那么honeycomb.io博客就有一个很好的介绍:你可以发明结构化日志

结构化日志记录后,您现在可以将某些属性列入黑名单,以便在运行时对其进行过滤。例如:

Blacklist = ["firstName", "lastName", "SSN"]
SSNRegex = r"^\d{3}-?\d{2}-?\d{4}$"
EmailRegex = r".+@.+";
class Logger {
  log(details: Map<string,string>) {
    const cleanedDetails = details.map( (key, value) => {
      if (Blacklist.contains(key) || 
          SSNRegex.match(value) || 
          EmailRegex.match(value)) {
        return (key, "<redacted>");
      }
      return (key, value);
    }
    console.log(JSON.stringify(cleanedDetails));
  }
}

在上面的记录器中,我们有一个基于黑名单和两个正则表达式的简单启发式,以跳过看起来像SSN或电子邮件的值。

问题

这并不能阻止某人做类似的事情:

logger.log({'pleaseLogThis': user.firstName});

要么

logger.log('{firstName:Joe}');

也可能有一个误报,其中一个日志记录语句过滤掉它不应该的东西 - 但根据我的经验,这是非常罕见的,很容易在开发早期捕获。

五、通过代码审查尽量避免

作为代码审查的一部分,审阅者应查找包含敏感数据的日志记录。如果您使用的是Pull Request Template,则可能需要在模板中设置一个复选框,以便审阅者确认他们已在更改中验证了日志记录语句。

什么可能出错

根据我的经验,审稿人倾向于掩盖日志记录。它需要文化的转变来注意并密切关注它们。

六、通过QA和自动化测试

虽然您的QA团队应该测试系统中的许多流程是否正常工作,但他们的测试并不止于此。如果测试是自动化的并且使用可预测的数据,则测试可以自动检查该数据是否未在日志中结束。例如,如果Web表单包含名字,姓氏和SSN字段,则在运行Selenium套件之后,测试还应在应用服务器日志中查找该名字,姓氏和SSN。

问题

QA团队通常没有正确的访问权限,甚至不知道要检查哪些系统。如果他们一直在进行黑盒测试,那么需要一些工作才能让他们加快速度。

七、在日志系统中配置告警

与#4和#6类似,您可以在日志记录系统中编写测试以查找某些数据模式。例如,查找SSN或搜索常见测试数据的正则表达式。此测试应该在您的staging或dev环境中就位,以便将代码提升为生产之前捕获它。

虽然这看起来有点矫枉过正,但我​​已经看到这种技术在它投入生产之前会发现许多可能的PII漏洞。如果您有一个复杂的系统,系统某个部分的微小变化可能会产生意想不到的后果,这些后果很难以其他方式捕获。

问题

您的登台环境的应用程序配置(例如日志级别)是否与生产相匹配?您是否将登录的日志记录到与生产相同的日志记录系统中?

有时,警报可能过于嘈杂,因为将为DEBUG级别的日志记录配置分段,这会导致更多消息。有时,团队会忽略分段提醒。根据我的经验,以与生产中相同的活力处理分段中的警报或中断非常重要。否则,你可能不会提前抓住这些东西。

总结

这些最佳实践可以帮助您将敏感数据保留在日志之外。它肯定不是一个完整的设置,可以让你为HIPAA或SOC2审计做好准备,但它们可以使你的创业公司在这方面有一个良好的立足点。即使您不在高度合规行业,您也应该认真对待保持客户数据的机密性。

但在一天结束时,清单不会使您的系统安全。您的公司和软件团队需要投资建立安全系统的文化。

如果您想了解有关日志记录和安全性的更多信息,OWASP拥有许多优秀的资源,包括Logging Cheat Sheet

我看到数据潜入日志的其他方式包括:

参考资料:https://medium.com/@joecrobak/seven-best-practices-for-keeping-sensitive-data-out-of-logs-3d7bbd12904

上一篇下一篇

猜你喜欢

热点阅读