Hi !欢迎阅读《爱彼迎数据隐私与安全工程》系列文章。我们会分为上中下三篇,介绍如何构建功能强大、自动化且可拓展的数据安全技术保障。
本篇是系列的第二篇,我们会介绍如何在 Airbnb 提供强大、自动化和可扩展的数据隐私和安全工程能力。
介绍
在本系列文章的第一篇中,我们介绍了数据保护平台(Data Protection Platform,以下简称 DPP),是一个使我们能符合国际法律和安全需求的数据保护平台。我们强调了解我们的数据是保护数据的必要组成部分,可以通过跟踪个人和敏感数据在我们生态系统中的存储位置来实现。在本篇文章中,我们将讨论公司在查找个人和敏感数据的确切位置时经常面临的挑战。许多公司依靠工程师手动追踪个人数据和敏感数据在内部系统如何流转以及流向何处,但依靠手动数据分类会带来不少挑战:
数据在不断迭代。这使工程师很难全面了解数据,及数据如何在公司的基础设施中流动。数据还会复制并存到不同的数据存储中,此外,随着产品的变化和新产品的出现,也会有新类型的数据产生。
手动分类更容易出错。工程师可能会忘记该数据资产是否包含个人数据,或者有些数据是用户自由输入,工程师并不知道里面会包含什么。
安全和个人隐私数据不断增加。对于新隐私法规和安全合规要求的新增数据元素,工程师须再次进行手动数据分类,造成了公司的高成本和低效率。
在代码库和各种数据存储中可能会泄漏密钥。工程师常用密钥包括生产环境API 密钥、供应商密钥和数据库登录凭证等。代码库中泄漏密钥是一个常见的问题,通常由于工程师意外或无意识地提交代码且没有被审批人发现。密钥一旦检入(check in)生产环境,想要找到它们就变成大海捞针,不容易被发现。
为了应对这些挑战,我们构建了数据分类工具来检测数据存储、日志和源代码中的个人和敏感数据。一起来看下我们的数据分类工具架构。具体来说,我们将深入研究 Inspekt 的技术组件,即数据存储和日志数据分类系统,以及 Angmar -- Github 企业版上的代码库的密钥检测和防护系统。
Inspekt:数据分类服务
Inspekt 是一个可扩展的自动化数据分类工具,可以确定我们生态系统中个人和敏感数据的存储位置。Inspekt 由两个服务组成:第一个服务称为任务创建器(Task Creator),它确定需要扫描的内容,第二个系统称为扫描器(Scanner),对数据进行采样和扫描以检测个人和敏感数据。
任务创建器(Task Creator)
任务创建器负责确定要扫描的内容,并将其拆分为任务,提供给扫描器。
Inspekt任务创建器会定期调用我们在之前的文章中提到的元数据服务 Madoka,以获取 Airbnb 的数据资产列表。这个服务会获取 MySQL 和 Hive 数据存储所有表格(table)的目录, 以及每个 AWS 账户的AWS S3存储桶目录及其对应的对象键(object key)列表。由于数据量庞大,任务创建器会从每个存储桶中随机抽取一小部分对象键。对于应用程序日志,它会获取 Airbnb 所有服务的列表以及存储日志的相应 Elasticsearch 集群。然后任务创建器为每个表/对象/应用程序创建一个SQS消息,也就是一个任务,并将其添加到扫描SQS队列中由扫描器消费。
扫描器(Scanner)…
扫描器(Scanner)负责对数据进行采样和扫描来检测个人信息。Inspekt 提供了一个接口来定义对采样数据的扫描方式和算法。我们会对每个数据都定义一种或多种扫描方法的组合作为“验证器(verifier)”。
Inspekt 目前支持四种扫描方式:
正则表达式 (Regexes):正则表达式适用于固定格式的数据,例如经度和纬度坐标、生日、电子邮件地址等。Inspekt 允许我们定义正则表达式来匹配数据资产元数据(例如列名、对象键名)或资产的内容。Inspekt 还允许我们将正则表达式定义为许可列表和拒绝列表。例如,我们可以定义一个正则表达式来检测列名包含“birthdate”、或者内容包含“birthdate”或内容不包含“birthdate”的数据资产。
前缀树(Tries):有些我们收集的数据元素并不遵循固定模式,例如名字和姓氏,无法使用正则表达式来检测。当数据元素存储在已知的数据存储中时,我们可以用 Aho-corasick 算法[1]使用前缀树对有限的数据样本进行子串匹配检测。
机器学习 (ML) 模型:由于一些原因,使用基于正则表达式的扫描无法准确或有效地检测到许多数据元素。首先,一些数据元素有不同的格式或无法详尽的内容量,例如邮寄地址。其次,作为一家在 200 多个国家开展业务的全球性公司,Airbnb 使用多种语言存储数据。第三,一些数据不是基于文本的,例如图像,因此无法使用常规扫描方法进行识别。基于机器学习的算法自然适合应对这些挑战。我们开发了不同的机器/深度学习模型,例如多任务 CNN、Bert-NER 和 WiDeText 分类模型,用于检测多个复杂数据元素。这些模型要么通过我们生产数据数据样本进行训练,例如 Airbnb 房源地址,要么通过公共数据集或大型文本预训练的模型进行训练。我们将这些模型托管在Airbnb 机器学习平台Bighead上,为 Inspekt 提供 API 接口,以通过机器学习扫描来检测每个数据元素。
硬编码方式:一些我们收集的数据遵循固定模式,但要么太复杂而无法在正则表达式中描述,要么已经存在高质量检测数据元素的开源解决方案。Inspekt 允许自定义代码来检测数据元素。例如,我们利用开源库创建了一个国际银行帐号 (IBAN) 的数据元素验证器。
在 Inspekt定义验证器为 JSON blob,并存储在扫描器读取的数据库中。这使我们能够不需要重新部署服务就可以简单修改现有验证器或添加新验证器来检测新数据元素。
下面是一个验证器配置示例,目的在于检测包含“birthdate”的列名或包含“birthdate”的内容:
{
"dataElementName": "birthdateKeyword",
"scanningMethods": [
{
"methodName": "birthdate_content_regex",
"methodType": "content_regex",
"contentRegexConfig": {
"allowList": [
"birthdate"
]
},
"methodName": "birthdate_colname_regex",
"methodType": "colname_regex",
"colNameRegexConfig": {
"allowList": [
"birthdate"
]
}
}
],
"evaluationExpression": "birthdate_content_regex || birthdate_colname_regex"
}
Inspekt扫描器是一个部署在Kubernetes 的分布式系统,可以根据工作负载(即队列中的任务)按需进行扩展。每个扫描器节点从 SQS 任务队列中提取任务消息。为了扫描器的稳健性,每条消息都会在队列中重新出现 N 次,直到扫描器节点把它删除。Scanner 架构图如下所示。
图 1:Inspekt 扫描器架构
每个 Inspekt 扫描器节点都会在启动时从数据库中获取并初始化验证器。验证器会定期刷新以更新或更改配置。验证器初始化后,每个扫描器节点从任务创建器创建的任务队列中提取一个任务。每个任务都包含要执行该任务的规范,即要扫描的数据资产、采样量等。然后节点将每个任务提交给线程池来执行采样和扫描作业。扫描作业运行如下:
Inspekt 扫描器连接到任务中指定的数据存储并进行数据采样。对于 MySQL来说,扫描器节点将连接到 MySQL 数据库并为每个表采样部分行。为了每次扫描不同行组,且不用全表扫描,我们随机生成一个值X,X小于主键最大值,然后选择主键 >= X 的行子集。对于 Hive,我们从最新分区中对每个表的行子集进行采样。对于服务日志,我们对每个服务每天的日志子集进行采样。为了更好地覆盖不同的日志,我们对 Elasticsearch 日志集群进行查询,从不同的日志记录点中选择日志。对于 S3,我们生成一个小于对象大小的随机偏移量,并从该偏移量开始采样一组可定制的字节。我们还支持跨 AWS 账户进行扫描。如果对象与扫描程序运行所在不同的 AWS 账户中,Inspekt 会自动使用适当的代入角色( Assume Role)IAM 权限从外部账户访问和读取对象。
对于数据存储中的每条采样数据,Inspekt扫描器会用每个验证器对数据进行验证,以确定是否找到任何匹配项。
Inspekt 扫描器将匹配结果存储在数据库中。对于每个匹配项,我们将存储找到匹配项的数据资产的元数据、匹配的内容以及匹配的验证器。我们还会将这些信息的子集存储在单独的表中,其中仅包含数据资产和匹配到的数据元素。为了保护数据的安全性和隐私性,我们会定期从匹配结果表中删除记录。
Inspekt 扫描器删除已扫描完成的SQS任务消息。
Inspekt质量测量服务
如上一篇博客所述,我们的数据保护平台依赖Inspekt的分类结果来触发保护。为了下游服务能信任并采纳 Inspekt 的分类结果,我们需要持续确保高质量地检测到每个数据元素。
过多的误报会影响收到告警的团队,也会影响我们团队的信誉。同时,过多的漏报则意味着我们没有成功找到所有出现的数据元素,从而引发隐私和安全问题。
质量测量策略…
为了持续监控和提高每个数据元素验证器的质量,我们构建了 Inspekt 质量测量服务来测量它们的精度、召回率和准确度。
我们将每个数据元素的正向数据和负向数据作为基础事实存储在 Inspekt 质量测量数据库中。然后对基础事实数据集运行验证程序。对正向数据,输出验证器生成的真正数(TP)和假反数(FN)。对负向数据,输出验证器生成的假正数(FP)和真反数(TN)。然后,我们可以从 TP、FN、FP、TN 计数中计算精度、召回率和准确率。
图 2: Inspekt质量计算
采样和填充测试数据…
如前所述,我们需要对于每个个人数据元素收集真正和真负数据集。为了使这些指标准确,用于测试的数据集必须尽可能全面,并且与生产数据相似。我们通过定期从以下来源采样数据来填充此数据集:
生产中的已知数据集:在线数据库或数据仓库中的某些列已知包含并代表特定数据元素,例如一个已知存储电子邮件的MySQL 列。我们可以将这些列作为真正数据来使用。
Inspekt 结果:当 Inspekt 运行并生成结果时,结果可以代表真正或假正。我们可以使用这些数据来填充数据集。
已知的自由格式/非结构化数据:我们的在线数据库或数据仓库中的某些列是自由格式的用户输入数据或非结构化 blob,例如消息、JSON 对象等。这些列可能包含任何类型的数据元素,是很好的测试数据源,可以确保我们的系统检测到非结构化格式数据和不同极端情况的数据。
生成的假合成数据:在我们的数据存储中有一些数据元素并不经常出现,例如用户的身高和体重,没有已知的数据列来存储它们。为了对这些数据元素有足够的测试数据,我们通过适当的格式生成假数据来填充我们的测试数据库。
标记
在对数据进行采样后,我们需要清楚每个样本代表真正或真负,再将其存储到我们的测试数据集中。因此我们使用 AWS Ground Truth 来手动标记采样数据。我们对于每个数据元素都制定了说明,并培训了 Airbnb 员工来正确标记每个样本为真或假。然后我们的系统会将每个数据元素采样的原始数据上传到 AWS S3,并通过适当的指示创建标记作业到 Ground Truth。一旦员工完成数据标记,标记后的输出将存储在 S3 存储桶中,供我们的系统读取。Inspekt 质量测量服务会定期检查存储桶以确定标记的数据是否准备好。如果准备好了,它会读取数据并存储在我们的测试数据集中,然后从 S3 中删除原始数据和标记数据。
图 3: Inspekt 测试标记管道
重新训练 ML 模型
Inspekt 质量测量服务的标记数据对于提高 Inspekt 验证器的性能具有重要价值。具体来说,标记的结果可以成为加强 Inspekt 机器学习模型性能的有用来源。我们将标记的数据输入到一些机器学习模型的训练样本中。在重新训练期间,新标记的数据与训练样本一起使用。每次重新训练后,相应的数据元素都会获得更好的模型。
Angmar:代码中的秘钥检测和防护?
在前面章节中,我们描述了 Inspekt 如何专注于检测数据存储中的个人和敏感数据。但是,公司代码库中也可能存在一些敏感数据,例如业务和基础设施秘钥,如果泄露给第三方,可能会导致严重漏洞。我们还创建一个名为 Angmar 的密钥检测解决方案,利用检测和防护来保护 Github 企业版 (GHE) 中 Airbnb的敏感数据。
架构
Angmar 由两部分组成,一个CI 检查(CI check)来检测推送(push)到 GHE 的密钥和一个Github 预提交(pre-commit)挂钩来防止密钥进入 GHE。
我们构建了一个 CI 检查来扫描 Airbnb GHE 服务器上的每个提交。当提交(commit)被推送到 GHE 时,在允许合并到主分支前,一个必须通过(Pass)的CI作业会启动。CI 作业下载最新开源库 Yelp/detect-secrets,并对提交(commit)中每个修改或添加的文件运行密钥扫描作业。一旦在 CI 作业检测到密钥,它就会触发数据保护平台创建 JIRA 票证(ticket)并自动将票证分配给代码开发者来解决问题。票证生成的细节将在本博客系列的第 3 部分中讨论。我们要求在时间限(SLA)内删除和更新所有生产环境密钥,并使用Airbnb开发的生产密钥管理工具Bagpiper再次签入(check in)。
图 4: Angmar 架构
然而,随着 CI 检查引导对代码库的每次推送,密钥暴露时间窗口仍然给公司基础设施带来一定的安全风险。此外,密钥轮换在有些情况下会花费高昂的工程时间和费用。因此,我们提出了主动的方法来阻止密钥进入 GHE,来清除密钥的暴露风险并且节省了密钥更换的工作量。
定制化
我们针对 Airbnb 情况对 detect-secrets 开源库进行了一些定制化:
我们在代码库插件中添加了一些 Airbnb 特定的密钥数据元素。
根据我们对库中误报检测的分析,一些测试密钥、部署密钥或占位符被错误地检测为密钥。我们添加了过滤路径逻辑来跳过提交(commit)中的这些文件。
我们还使用散列算法部署了去重逻辑,来减少在不同提交(commit)中对同一文件进行修改而导致的重票证。
在极少数发生误报的情况下,我们允许开发人员跳过某些代码行或某些文件,以避免阻止紧急代码合并到生产中。安全团队会定期检查跳过的代码,以确保没有忽略掉任何密钥。
后期工作
我们正在不断改进和扩展 Inspekt 和 Angmar来扫描更多数据源并检测更多隐私和敏感数据元素。我们目前正在探索和开展的一些举措包括:
扫描 Thrift 接口描述语言 API 的请求和响应,跟踪个人和敏感数据在服务之间流动。
扫描我们的第三方应用程序,例如 Google Drive、Box,以了解数据如何传递到第三方应用程序以及数据如何在内部和外部进行访问的。
扩展支持更多的Airbnb使用的数据存储的扫描能力,例如 DynamoDB、Redis 等。
备选方案
市场上有一些针对数据分类的商业解决方案。在构建我们的解决方案之前,我们评估了一些供应商来决定是否可以利用现有工具而不用构建自己的方案。出于以下原因,我们决定构建内部解决方案:
数据存储覆盖范围:我们需要一个可以覆盖我们生态系统中存在的大部分数据存储的工具,为一部分数据搭建定制化工具需要的工作量和为所有的数据存储所需的工作量几乎相同。大多数供应商仅支持扫描 SAAS 应用程序和 S3 存储桶。
自定义扫描:我们需要自定义扫描算法。这一点很重要,因为我们要确保我们可以扫描所有个人和敏感数据元素,并确保对所有数据元素处理达到最佳性能(精度、召回率、准确性)。许多供应商支持针对自定义正则表达式进行扫描,但没有一家支持针对自定义 ML 模型进行扫描。
成本效率:针对我们的实现目标,构建我们的解决方案比使用商业解决方案更具成本效益。
结论
在第二篇文章中,我们深入探讨了数据分类系统的动机和架构,使我们能够大规模检测个人和敏感数据。在我们的下一篇文章中,我们将聚焦如何使用数据保护平台来实现各种安全和隐私用例。
引用
1. Aho-corasick 算法:https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm
作者:Elizabeth Nammour,Pinyao Guo,Wendy Jin,译者:Yang Li,校对:Ting Jiao,Yiqi Jia,Wei Ji。
Inspekt 和 Angmar 的实现和顺利应用要感谢爱彼迎数据安全团队、数据治理团队、AI labs 以及所有参与到项目中的成员。
如果你想了解关于爱彼迎技术的更多进展,欢迎关注我们的 Github 账号(https://github.com/airbnb/) 以及微信公众号(爱彼迎技术团队)