10

成功识别日志中的数据泄漏漏洞并对其进行缓解

 3 years ago
source link: http://4hou.win/wordpress/?p=61518
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

我会在本文介绍我是如何与r2c的另一位开发人员成功识别日志中的数据泄漏,从而修复了该漏洞并彻底杜绝其今后的再发生,整个过程只需几个小时就可以完成了。

作为一名开发人员和工程经理,我一直痴迷于寻找一种可以不需要安全团队完全参与,即可快速解决整个涉及组织安全漏洞的方法。

为什么要这么做呢?好处有很多:

1.可以快速解决组织出现的安全漏洞。在实践过程中,该方法可以大大加快安全防御的速度,以至于我们可以在识别出漏洞后的几分钟内建立起安全的防护措施,如果是走组织流程,则安全漏洞则会持续数天或数周。

2.当开发人员可以轻松地自行解决安全漏洞时,它可以使安全团队腾出精力来专注于整个组织的“全局”安全性。我希望安全工程师考虑如何选择框架、设置工具、帮助实现安全体系结构,以及构建深度防御,而不是找到我在本文所述的XSS漏洞。

我将以上过程称为“self-service DevSec。

接下来,我将介绍我们在日常开发工作过程中遇到的一个安全漏洞。我将讨论我们如何发现此漏洞的,以及如何在短短几个小时内修复整个安全漏洞,并使用Semgrep防止该漏洞再次发生。Semgrep是一个开源工具,用于使用熟悉的语法进行轻量级静态分析。

上个月,我正在与r2c的另一位工程师Clara McCreery一起调试Flask Web应用验证流程。就像许多工程师面临着令人困惑的调试问题一样,我们的第一步就是将Web应用程序放入调试日志记录。

具体来说,我们想知道数据库操作的情况,因此我们将ORM(在本例中,我们使用SQLAlchemy)设置为INFO级别的日志记录,方法如下:

logging.getLogger("sqlalchemy.engine.base.Engine").setLevel(logging.INFO)

这会将SQLAlchemy配置为记录所有SQL语句以及传递的参数,让我们看一下我们看到的一些输出结果:

INFO:werkzeug:127.0.0.1 - - [25/Sep/2020 11:50:01] "POST /api/auth/authenticate HTTP/1.1" 200 -
INFO:sqlalchemy.engine.base.Engine:BEGIN (implicit)
INFO:sqlalchemy.engine.base.Engine:SELECT token.id AS token_id, token.token AS token_token, token.name AS token_name
FROM token
WHERE token.token = %(token_1)s
 LIMIT %(param_1)sINFO:sqlalchemy.engine.base.Engine:{'token_1': $2a$10$KVsyW1jjKn.pvkVi3w9Rn.1mwnZFd7F2SFveGDG8flIhbe.MoJH4G, 'param_1': 1}

我们绝对不应该记录令牌,即使已安全地对其进行哈希处理。在此示例中,处于讲解的目的,实际令牌值已更改。

首先要制定一个计划

至此,我们已经确定了一个安全漏洞,并且希望在保留检查日志能力的同时修复此漏洞。具体步骤如下:

1.缓解当前的安全漏洞;

2.寻找一个永久的解决方案,以备不时之需。永久的解决方案意味着对我们的系统进行深层次的改变。理想情况下,该解决方案是在整个组织中自动化和无缝的。

3.添加一种机制来强制我们的解决方案在整个组织范围内使用。

接下来,我将指导你完成每个步骤。需要注意的是,我们能够在几个小时内完成整个流程,而无需与安全团队合作。

缓解当前的安全漏洞

这里的缓解措施非常简单,因为我们已经知道了漏洞的根本原因,为此可以快速还原日志记录的更改过程。然后,我们可以对日志进行快速审核,以确保仅泄漏了开发测试令牌。

永久解决方案

那我们如何防止SQLAlchemy记录敏感数据?

第一步是阅读文档。快速搜索“引擎日志中的sqlalchemy隐藏参数”将我们链接到SQLAlchemy Engine文档。稍后进行详细阅读,这样我们就发现了hide_parameters标志,该标志防止日志记录框架在日志或异常中发出任何参数。

虽然这肯定可以防止发现的安全漏洞,但对我们来说信息量太小了,因为我们想知道例如数据库ID等信息,以便进行调试。

真正的解决方案

然后,我们检查了相关的SQLAlchemy源代码,相关代码在sqlalchemy / engine / base.py中:

RFjU7bz.png!mobile

sql_util._repr_params依次运行:

YNr2UvA.png!mobile

通过研究trunc,我们发现它通过将参数的repr截断为最大字符数来转换参数值,这意味着我们应该重写参数对象的repr方法以防止敏感日志记录。

此时,我们像优秀的工程师一样,使用了一条懒惰的策略,因为我发现的这个GitHub漏洞,Mike Bayer已经发布了一个很好的解决方案,所以我就进行了一些复制,关键代码如下:

AjqU7n.png!mobile

这段代码的作用是什么?你可以发现它用新的ObfuscatedString.Repr参数替换了我们原来的str参数。登录时或发出异常消息时,该字符串将替换为我们的********。由于参数仍然被绑定为原始字符串(通过impl = types.String),因此仍然插入和从数据库中选择正确的值。

要使用这个新的字段类型,我们设置令牌的字段类型如下:

32imquM.png!mobile

然后,我们重新启用INFO日志记录,并检查我们是否正确混淆了文本:

INFO:werkzeug:127.0.0.1 - - [25/Sep/2020 13:48:55] "GET /api/agent/deployments/1/policies HTTP/1.1" 200 -
INFO:sqlalchemy.engine.base.Engine:BEGIN (implicit)
INFO:sqlalchemy.engine.base.Engine:SELECT token.id AS token_id, token.token AS token_token, token.name AS token_name
FROM token
WHERE token.token = %(token_1)s
 LIMIT %(param_1)s
INFO:sqlalchemy.engine.base.Engine:{'token_1': ********, 'param_1': 1}

为了完整起见,我们还在开发数据库控制台中验证了是否存储和检索了正确的值。

执行过程

应该说,我们已经暂时解决了安全漏洞,以便可以重新调试原始的身份验证漏洞。但要彻底修复整个漏洞。我们将如何做?

以下有一些想法,我相信我们都曾经遇到过:

1.在安全审查中阻止对SQLAlchemy模型的所有提交。

2.为所有开发人员举办年度安全培训,包括记录敏感数据的漏洞。

3.每周审核日志。

4.向你的SAST供应商提出漏洞,要求他们添加检查以捕获敏感记录的数据。

如果要从这篇博客文章中得出一个中心结论的话,那就是:这些都不是理想的解决方案,原因如下:

4.1阻止提交会在开发过程中引入不必要的拖延,降低开发速度,并会分散安全团队的注意力。

4.2安全培训是安全计划的重要组成部分,也是让开发人员意识到不断发展的安全威胁的必要条件,但是人类的记忆力很差,我们可能会忘记几个月甚至几天前听到的事情。

4.3定期审核(例如阻止提交)会给几乎肯定是超负荷的安全团队带来沉重的工作量;

4.4你的SAST提供商当然会欢迎你的建议,但是你会依赖他们的软件发布周期,并且可能几个月都看不到可用的检查。此外,如果你的漏洞是特定于某个领域的,则实施广泛地检查甚至没有意义。

幸运的是,Semgrep为我们提供了一个简单的解决方案:在代码中定义一个不变量,并在每次CI运行时使用Semgrep扫描对其进行强制执行。

在r2c中,我们使用GitHub操作在每个合并请求上运行Semgrep。我们使用由Semgrep .dev管理的管理策略、规则字段表和通知设置来定义Semgrep应该运行哪些检查。

为了保证我们的代码不会再出现问题,我访问了semgrep.dev/editor并编写了一个快速规则来检测潜在的不安全日志SQLAlchemy字段。

这是Semgrep的YAML定义语言中的规则定义:

VFNzY3F.png!mobile

这个规则有什么作用?详细解释如下:

id:我们为规则提供了一个简洁的描述性ID,以便任何在编辑器或CI输出中看到它的开发人员都可以轻松参考。

patterns:这由两部分组成:

pattern:此表达式告诉Semgrep如何在我们的代码库(在此示例中,我们的SQLAlchemy实例称为db)中查找具有String字段类型的任何SQLAlchemy ORM字段定义,它还将字段名称绑定到名为COLUMN的元变量。

metavvariable -regex:这个表达式告诉Semgrep只有在字段metavariable包含单词片段(如token、email、key或secret)时才报告匹配。正则表达式包含了很多细节声明,以防止我们匹配不相关的单词,如keyboard。

message:当Semgrep匹配我们的模式时,我们希望确保我们解释检测到的漏洞是什么,为什么它是一个漏洞,以及如何修复它。这些信息将有助于开发人员独立解决漏洞,而不会造成混乱或不必要的误读。

severity:你可以自定义你领域中任何漏洞的严重程度。

然后快速地按下“部署到策略”按钮,就可以保证所有的web应用程序都得到了保护。

通过我们的VS Code扩展将Semgrep集成到编程工作流中的开发人员也会开始在他们的IDE中产生效果。

请注意,此解决方案是有意迭代的:我们可能会发现更多字段名称被标识为敏感字段,或者还希望包含db.Text类型。幸运的是,这是一个快速修订,并根据需要重新部署。

总结

在这篇文章中,我演示了你作为一名开发人员或管理人员如何使用轻量级静态分析(如Semgrep)来帮助在代码中强制执行不变量。

在r2c中,我们习惯性地使用Semgrep来防止自己重复犯错误:意外地使调试器处于提交状态?有一条规则可以防止这种情况发生。当我们发现导入某个库会减慢程序的初始化速度时,我们编写了一条规则来确保它被延迟加载。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK